From 45daa065f45145d24b58be91662235b3769f7c8e Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Tue, 14 Jan 2025 15:33:50 -0700 Subject: [PATCH 1/3] add min and max for ninterp stuff --- Cargo.lock | 2 +- fastsim-core/Cargo.toml | 2 +- fastsim-core/src/traits.rs | 100 +++++++++++++++++++++++++++++-- fastsim-core/src/utils/interp.rs | 1 + fastsim-core/src/utils/mod.rs | 3 + 5 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 fastsim-core/src/utils/interp.rs diff --git a/Cargo.lock b/Cargo.lock index d5378adb..52d33d2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,7 +564,7 @@ dependencies = [ "include_dir", "itertools 0.12.1", "lazy_static", - "ndarray 0.15.6", + "ndarray 0.16.1", "ninterp", "nohash-hasher", "numpy", diff --git a/fastsim-core/Cargo.toml b/fastsim-core/Cargo.toml index 5f5d2c77..bc8e62b5 100644 --- a/fastsim-core/Cargo.toml +++ b/fastsim-core/Cargo.toml @@ -32,7 +32,7 @@ include_dir = { version = "0.7.3", optional = true } itertools = "0.12.0" lazy_static = "1.4.0" regex = "1.10.3" -ndarray = "0.15.6" +ndarray = "0.16.1" toml = { version = "0.8.12", optional = true } derive_more = { version = "1.0.0", features = ["from_str", "from", "is_variant", "try_into"] } ureq = { version = "2.9.1", optional = true } diff --git a/fastsim-core/src/traits.rs b/fastsim-core/src/traits.rs index 042e880c..633ded27 100644 --- a/fastsim-core/src/traits.rs +++ b/fastsim-core/src/traits.rs @@ -31,14 +31,60 @@ impl Min for &[f64] { } impl Min for Vec { fn min(&self) -> anyhow::Result { - Ok(self.iter().fold(f64::INFINITY, |acc, curr| acc.min(*curr))) + self.as_slice().min() } } -impl Min for Vec<&f64> { +impl Min for &[&f64] { fn min(&self) -> anyhow::Result { Ok(self.iter().fold(f64::INFINITY, |acc, curr| acc.min(**curr))) } } +impl Min for Vec<&f64> { + fn min(&self) -> anyhow::Result { + self.as_slice().min() + } +} +impl Min for &[Vec] { + fn min(&self) -> anyhow::Result { + self + .iter() + .map(|v| v.min()) + .try_fold(f64::INFINITY, |acc, x| Ok(acc.min(x?))) + } +} +impl Min for Vec> { + fn min(&self) -> anyhow::Result { + self.as_slice().min() + } +} +impl Min for &[Vec>] { + fn min(&self) -> anyhow::Result { + self.iter() + .map(|v| v.min()) + .try_fold(f64::INFINITY, |acc, x| Ok(acc.min(x?))) + } +} +impl Min for Vec>> { + fn min(&self) -> anyhow::Result { + self.as_slice().min() + } +} +impl Min for &ArrayD { + fn min(&self) -> anyhow::Result { + Ok(self.iter().fold(f64::INFINITY, |acc, x| acc.min(*x))) + } +} +impl Min for Interpolator { + fn min(&self) -> anyhow::Result { + match self { + Interpolator::Interp0D(value) => Ok(*value), + Interpolator::Interp1D(interp) => interp.f_x().min(), + Interpolator::Interp2D(interp) => interp.f_xy().min(), + Interpolator::Interp3D(interp) => interp.f_xyz().min(), + Interpolator::InterpND(interp) => interp.values().min(), + } + } +} pub trait Max { fn max(&self) -> anyhow::Result; @@ -51,17 +97,61 @@ impl Max for &[f64] { } } impl Max for Vec { + fn max(&self) -> anyhow::Result { + self.as_slice().max() + } +} +impl Max for &[&f64] { fn max(&self) -> anyhow::Result { Ok(self .iter() - .fold(f64::NEG_INFINITY, |acc, curr| acc.max(*curr))) + .fold(f64::NEG_INFINITY, |acc, curr| acc.max(**curr))) } } impl Max for Vec<&f64> { fn max(&self) -> anyhow::Result { - Ok(self + self.as_slice().max() + } +} +impl Max for &[Vec] { + fn max(&self) -> anyhow::Result { + self .iter() - .fold(f64::NEG_INFINITY, |acc, curr| acc.max(**curr))) + .map(|v| v.max()) + .try_fold(f64::NEG_INFINITY, |acc, x| Ok(acc.max(x?))) + } +} +impl Max for Vec> { + fn max(&self) -> anyhow::Result { + self.as_slice().max() + } +} +impl Max for &[Vec>] { + fn max(&self) -> anyhow::Result { + self.iter() + .map(|v| v.max()) + .try_fold(f64::NEG_INFINITY, |acc, x| Ok(acc.max(x?))) + } +} +impl Max for Vec>> { + fn max(&self) -> anyhow::Result { + self.as_slice().max() + } +} +impl Max for &ArrayD { + fn max(&self) -> anyhow::Result { + Ok(self.iter().fold(f64::NEG_INFINITY, |acc, x| acc.max(*x))) + } +} +impl Max for Interpolator { + fn max(&self) -> anyhow::Result { + match self { + Interpolator::Interp0D(value) => Ok(*value), + Interpolator::Interp1D(interp) => interp.f_x().max(), + Interpolator::Interp2D(interp) => interp.f_xy().max(), + Interpolator::Interp3D(interp) => interp.f_xyz().max(), + Interpolator::InterpND(interp) => interp.values().max(), + } } } diff --git a/fastsim-core/src/utils/interp.rs b/fastsim-core/src/utils/interp.rs new file mode 100644 index 00000000..8a0d9950 --- /dev/null +++ b/fastsim-core/src/utils/interp.rs @@ -0,0 +1 @@ +use crate::imports::*; diff --git a/fastsim-core/src/utils/mod.rs b/fastsim-core/src/utils/mod.rs index 7c161c76..409f98c6 100755 --- a/fastsim-core/src/utils/mod.rs +++ b/fastsim-core/src/utils/mod.rs @@ -2,6 +2,9 @@ use crate::imports::*; use paste::paste; use regex::Regex; +pub mod interp; +pub use interp::*; + impl Init for ninterp::Interpolator {} impl SerdeAPI for ninterp::Interpolator { const RESOURCE_PREFIX: &'static str = "interpolators"; From 3f747693dd419d446e6ec46b178adaa4c07a8d90 Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Tue, 14 Jan 2025 16:06:09 -0700 Subject: [PATCH 2/3] downgrade ndarray dep --- Cargo.lock | 2 +- fastsim-core/Cargo.toml | 2 +- fastsim-core/src/traits.rs | 26 ++++++++++---------------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52d33d2d..d5378adb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,7 +564,7 @@ dependencies = [ "include_dir", "itertools 0.12.1", "lazy_static", - "ndarray 0.16.1", + "ndarray 0.15.6", "ninterp", "nohash-hasher", "numpy", diff --git a/fastsim-core/Cargo.toml b/fastsim-core/Cargo.toml index bc8e62b5..5f5d2c77 100644 --- a/fastsim-core/Cargo.toml +++ b/fastsim-core/Cargo.toml @@ -32,7 +32,7 @@ include_dir = { version = "0.7.3", optional = true } itertools = "0.12.0" lazy_static = "1.4.0" regex = "1.10.3" -ndarray = "0.16.1" +ndarray = "0.15.6" toml = { version = "0.8.12", optional = true } derive_more = { version = "1.0.0", features = ["from_str", "from", "is_variant", "try_into"] } ureq = { version = "2.9.1", optional = true } diff --git a/fastsim-core/src/traits.rs b/fastsim-core/src/traits.rs index 633ded27..656b13f3 100644 --- a/fastsim-core/src/traits.rs +++ b/fastsim-core/src/traits.rs @@ -46,8 +46,7 @@ impl Min for Vec<&f64> { } impl Min for &[Vec] { fn min(&self) -> anyhow::Result { - self - .iter() + self.iter() .map(|v| v.min()) .try_fold(f64::INFINITY, |acc, x| Ok(acc.min(x?))) } @@ -69,11 +68,6 @@ impl Min for Vec>> { self.as_slice().min() } } -impl Min for &ArrayD { - fn min(&self) -> anyhow::Result { - Ok(self.iter().fold(f64::INFINITY, |acc, x| acc.min(*x))) - } -} impl Min for Interpolator { fn min(&self) -> anyhow::Result { match self { @@ -81,7 +75,10 @@ impl Min for Interpolator { Interpolator::Interp1D(interp) => interp.f_x().min(), Interpolator::Interp2D(interp) => interp.f_xy().min(), Interpolator::Interp3D(interp) => interp.f_xyz().min(), - Interpolator::InterpND(interp) => interp.values().min(), + Interpolator::InterpND(interp) => Ok(interp + .values() + .iter() + .fold(f64::INFINITY, |acc, x| acc.min(*x))), } } } @@ -115,8 +112,7 @@ impl Max for Vec<&f64> { } impl Max for &[Vec] { fn max(&self) -> anyhow::Result { - self - .iter() + self.iter() .map(|v| v.max()) .try_fold(f64::NEG_INFINITY, |acc, x| Ok(acc.max(x?))) } @@ -138,11 +134,6 @@ impl Max for Vec>> { self.as_slice().max() } } -impl Max for &ArrayD { - fn max(&self) -> anyhow::Result { - Ok(self.iter().fold(f64::NEG_INFINITY, |acc, x| acc.max(*x))) - } -} impl Max for Interpolator { fn max(&self) -> anyhow::Result { match self { @@ -150,7 +141,10 @@ impl Max for Interpolator { Interpolator::Interp1D(interp) => interp.f_x().max(), Interpolator::Interp2D(interp) => interp.f_xy().max(), Interpolator::Interp3D(interp) => interp.f_xyz().max(), - Interpolator::InterpND(interp) => interp.values().max(), + Interpolator::InterpND(interp) => Ok(interp + .values() + .iter() + .fold(f64::NEG_INFINITY, |acc, x| acc.max(*x))), } } } From dafc457188e399490b0f85a92c97b05a3ce6bf83 Mon Sep 17 00:00:00 2001 From: Kyle Carow Date: Tue, 14 Jan 2025 22:12:54 -0700 Subject: [PATCH 3/3] max and range methods --- fastsim-core/src/utils/interp.rs | 106 +++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/fastsim-core/src/utils/interp.rs b/fastsim-core/src/utils/interp.rs index 8a0d9950..265b43db 100644 --- a/fastsim-core/src/utils/interp.rs +++ b/fastsim-core/src/utils/interp.rs @@ -1 +1,107 @@ use crate::imports::*; + +/// Methods for proportionally scaling interpolator function data +pub trait InterpolatorMethods { + fn set_min(&mut self, min: f64) -> anyhow::Result<()>; + fn set_max(&mut self, max: f64) -> anyhow::Result<()>; + fn set_range(&mut self, range: f64) -> anyhow::Result<()>; +} + +impl InterpolatorMethods for Interpolator { + fn set_min(&mut self, min: f64) -> anyhow::Result<()> { + let old_min = self.min()?; + match self { + Interpolator::Interp0D(value) => Ok(*value = min), + Interpolator::Interp1D(interp) => { + todo!() + } + Interpolator::Interp2D(interp) => { + todo!() + } + Interpolator::Interp3D(interp) => { + todo!() + } + Interpolator::InterpND(interp) => { + todo!() + } + } + } + + fn set_max(&mut self, max: f64) -> anyhow::Result<()> { + let old_max = self.max()?; + match self { + Interpolator::Interp0D(value) => Ok(*value = max), + Interpolator::Interp1D(interp) => { + Ok(interp.set_f_x(interp.f_x().iter().map(|x| x * max / old_max).collect())?) + } + Interpolator::Interp2D(interp) => Ok(interp.set_f_xy( + interp + .f_xy() + .iter() + .map(|v| v.iter().map(|x| x * max / old_max).collect()) + .collect(), + )?), + Interpolator::Interp3D(interp) => Ok(interp.set_f_xyz( + interp + .f_xyz() + .iter() + .map(|v0| { + v0.iter() + .map(|v1| v1.iter().map(|x| x * max / old_max).collect()) + .collect() + }) + .collect(), + )?), + Interpolator::InterpND(interp) => { + Ok(interp.set_values(interp.values().map(|x| x * max / old_max))?) + } + } + } + + fn set_range(&mut self, range: f64) -> anyhow::Result<()> { + let old_max = self.max()?; + let old_range = old_max - self.min()?; + ensure!(old_range != 0., "Cannot modify range when min == max"); + match self { + Interpolator::Interp0D(_value) => unreachable!("The above `ensure` should trigger"), + Interpolator::Interp1D(interp) => Ok(interp.set_f_x( + interp + .f_x() + .iter() + .map(|x| old_max + (x - old_max) * range / old_range) + .collect(), + )?), + Interpolator::Interp2D(interp) => Ok(interp.set_f_xy( + interp + .f_xy() + .iter() + .map(|v| { + v.iter() + .map(|x| old_max + (x - old_max) * range / old_range) + .collect() + }) + .collect(), + )?), + Interpolator::Interp3D(interp) => Ok(interp.set_f_xyz( + interp + .f_xyz() + .iter() + .map(|v0| { + v0.iter() + .map(|v1| { + v1.iter() + .map(|x| old_max + (x - old_max) * range / old_range) + .collect() + }) + .collect() + }) + .collect(), + )?), + Interpolator::InterpND(interp) => Ok(interp.set_values( + interp + .values() + .map(|x| old_max + (x - old_max) * range / old_range), + )?), + } + } +}