From dbf32f76acd54bf56a934e59498ab7cc697af90a Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:19:14 +0800 Subject: [PATCH 01/13] Add error enums The error variants here should guide the user as to the wrong use of functions, instead of taking down the entire program with a panic, which is especially necessary in an embedded environment. --- sci-rs-core/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 sci-rs-core/src/lib.rs diff --git a/sci-rs-core/src/lib.rs b/sci-rs-core/src/lib.rs new file mode 100644 index 00000000..e9c4dc0f --- /dev/null +++ b/sci-rs-core/src/lib.rs @@ -0,0 +1,35 @@ +//! Core library for sci-rs. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "alloc")] +extern crate alloc; + +use core::{error, fmt}; + +/// Errors raised whilst running sci-rs. +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// Argument parsed into function were invalid. + #[cfg(feature = "alloc")] + InvalidArg { + /// The invalid arg + arg: alloc::string::String, + /// Explaining why arg is invalid. + reason: alloc::string::String, + }, + /// Two or more optional arguments passed into functions conflict. + #[cfg(feature = "alloc")] + ConfictArg { + /// Explaining what arg is invalid. + reason: alloc::string::String, + }, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} + +impl error::Error for Error {} From 66d829a73ee7c0277cd7eb1bee7d061eaecd8870 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:24:10 +0800 Subject: [PATCH 02/13] Add non-allocating variants of Error enum variants This however does not specify to the end user which arguments are raising the error. Fix "Conflict" typo in Error enum variant --- sci-rs-core/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sci-rs-core/src/lib.rs b/sci-rs-core/src/lib.rs index e9c4dc0f..14c5071d 100644 --- a/sci-rs-core/src/lib.rs +++ b/sci-rs-core/src/lib.rs @@ -18,12 +18,18 @@ pub enum Error { /// Explaining why arg is invalid. reason: alloc::string::String, }, + /// Argument parsed into function were invalid. + #[cfg(not(feature = "alloc"))] + InvalidArg, /// Two or more optional arguments passed into functions conflict. #[cfg(feature = "alloc")] - ConfictArg { + ConflictArg { /// Explaining what arg is invalid. reason: alloc::string::String, }, + /// Two or more optional arguments passed into functions conflict. + #[cfg(not(feature = "alloc"))] + ConflictArg, } impl fmt::Display for Error { From 42c29033deb334f0aadb2dc000289884ab7c3da5 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Fri, 25 Apr 2025 13:39:02 +0800 Subject: [PATCH 03/13] Add ndarray_conv to core ndarray_conv provides linear and FFT convolution for N-dimensional tensors. This commit thus introduces into core under a "num_rs" namespace, since there are many scipy functions that use numpy functions. The ConvolveMode enum is thus moved, and the relevant enum variants of ndarray_conv is also provisioned by means of `.into()`. --- sci-rs-core/Cargo.toml | 29 +++++++++++++++++++ sci-rs-core/src/lib.rs | 2 ++ sci-rs-core/src/num_rs/convolve/mod.rs | 11 +++++++ .../src/num_rs/convolve/ndarray_conv_binds.rs | 12 ++++++++ sci-rs-core/src/num_rs/mod.rs | 2 ++ sci-rs/Cargo.toml | 1 + sci-rs/src/signal/convolve.rs | 10 +------ 7 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 sci-rs-core/Cargo.toml create mode 100644 sci-rs-core/src/num_rs/convolve/mod.rs create mode 100644 sci-rs-core/src/num_rs/convolve/ndarray_conv_binds.rs create mode 100644 sci-rs-core/src/num_rs/mod.rs diff --git a/sci-rs-core/Cargo.toml b/sci-rs-core/Cargo.toml new file mode 100644 index 00000000..8ff58964 --- /dev/null +++ b/sci-rs-core/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "sci-rs-core" +version = "0.0.0" +edition = "2021" +authors = ["Jacob Trueb "] +description = "Core library for sci-rs internals." +license = "MIT OR Apache-2.0" +repository = "https://github.com/qsib-cbie/sci-rs.git" +homepage = "https://github.com/qsib-cbie/sci-rs.git" +readme = "../README.md" +keywords = ["scipy", "dsp", "signal", "filter", "design"] +categories = ["science", "mathematics", "no-std", "embedded"] + + +[package.metadata.docs.rs] +all-features = true + +[features] +default = ['alloc'] + +# Allow allocating vecs, matrices, etc. +alloc = [] + +# Enable FFT and standard library features +std = ['alloc'] + +[dependencies] +ndarray = { version = "0.16.1", default-features = false } +ndarray-conv = { version = "0.4.1" } diff --git a/sci-rs-core/src/lib.rs b/sci-rs-core/src/lib.rs index 0cf6a8d8..8de2ed82 100644 --- a/sci-rs-core/src/lib.rs +++ b/sci-rs-core/src/lib.rs @@ -60,3 +60,5 @@ impl fmt::Display for Error { } impl error::Error for Error {} + +pub mod num_rs; diff --git a/sci-rs-core/src/num_rs/convolve/mod.rs b/sci-rs-core/src/num_rs/convolve/mod.rs new file mode 100644 index 00000000..e927424e --- /dev/null +++ b/sci-rs-core/src/num_rs/convolve/mod.rs @@ -0,0 +1,11 @@ +mod ndarray_conv_binds; + +/// Convolution mode determines behavior near edges and output size +pub enum ConvolveMode { + /// Full convolution, output size is `in1.len() + in2.len() - 1` + Full, + /// Valid convolution, output size is `max(in1.len(), in2.len()) - min(in1.len(), in2.len()) + 1` + Valid, + /// Same convolution, output size is `in1.len()` + Same, +} diff --git a/sci-rs-core/src/num_rs/convolve/ndarray_conv_binds.rs b/sci-rs-core/src/num_rs/convolve/ndarray_conv_binds.rs new file mode 100644 index 00000000..b0f38c79 --- /dev/null +++ b/sci-rs-core/src/num_rs/convolve/ndarray_conv_binds.rs @@ -0,0 +1,12 @@ +use super::ConvolveMode; +use ndarray_conv::ConvMode; + +impl From for ConvMode { + fn from(value: ConvolveMode) -> Self { + match value { + ConvolveMode::Full => ConvMode::Full, + ConvolveMode::Same => ConvMode::Same, + ConvolveMode::Valid => ConvMode::Valid, + } + } +} diff --git a/sci-rs-core/src/num_rs/mod.rs b/sci-rs-core/src/num_rs/mod.rs new file mode 100644 index 00000000..424dc967 --- /dev/null +++ b/sci-rs-core/src/num_rs/mod.rs @@ -0,0 +1,2 @@ +mod convolve; +pub use convolve::*; diff --git a/sci-rs/Cargo.toml b/sci-rs/Cargo.toml index 697e7933..1269f678 100644 --- a/sci-rs/Cargo.toml +++ b/sci-rs/Cargo.toml @@ -36,6 +36,7 @@ lstsq = { version = "0.6.0", default-features = false } rustfft = { version = "6.2.0", optional = true } kalmanfilt = { version = "0.3.0", default-features = false } gaussfilt = { version = "0.1.3", default-features = false } +sci-rs-core = { path = "../sci-rs-core" } [dev-dependencies] approx = "0.5.1" diff --git a/sci-rs/src/signal/convolve.rs b/sci-rs/src/signal/convolve.rs index b527805e..7e0486b5 100644 --- a/sci-rs/src/signal/convolve.rs +++ b/sci-rs/src/signal/convolve.rs @@ -2,15 +2,7 @@ use nalgebra::Complex; use num_traits::{Float, FromPrimitive, Signed, Zero}; use rustfft::{FftNum, FftPlanner}; -/// Convolution mode determines behavior near edges and output size -pub enum ConvolveMode { - /// Full convolution, output size is `in1.len() + in2.len() - 1` - Full, - /// Valid convolution, output size is `max(in1.len(), in2.len()) - min(in1.len(), in2.len()) + 1` - Valid, - /// Same convolution, output size is `in1.len()` - Same, -} +pub use sci_rs_core::num_rs::ConvolveMode; /// Performs FFT-based convolution on two slices of floating point values. /// From 54e75e48a7a696c2c30df4784d28d9e4a4f9aace Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:46:03 +0800 Subject: [PATCH 04/13] Add Result "alias" to core --- sci-rs-core/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sci-rs-core/src/lib.rs b/sci-rs-core/src/lib.rs index 14c5071d..202b7be6 100644 --- a/sci-rs-core/src/lib.rs +++ b/sci-rs-core/src/lib.rs @@ -7,6 +7,8 @@ extern crate alloc; use core::{error, fmt}; +pub type Result = core::result::Result; + /// Errors raised whilst running sci-rs. #[derive(Debug, PartialEq, Eq)] pub enum Error { From 2d1f079d65258f7ca8996ee2c52c4b5797ac5d26 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:55:54 +0800 Subject: [PATCH 05/13] Add error messages Display trait is for when `main` ends and an error message is shown to the end user. --- sci-rs-core/src/lib.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/sci-rs-core/src/lib.rs b/sci-rs-core/src/lib.rs index 202b7be6..0cf6a8d8 100644 --- a/sci-rs-core/src/lib.rs +++ b/sci-rs-core/src/lib.rs @@ -4,6 +4,8 @@ #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "alloc")] +use alloc::format; use core::{error, fmt}; @@ -36,7 +38,24 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - todo!() + write!( + f, + "{}", + match self { + #[cfg(feature = "alloc")] + Error::InvalidArg { arg, reason } => + format!("Invalid Argument on arg = {} with reason = {}", arg, reason), + #[cfg(not(feature = "alloc"))] + Error::InvalidArg => + "There were invalid arguments. Reasons not shown without `alloc` feature.", + #[cfg(feature = "alloc")] + Error::ConflictArg { reason } => + format!("Conflicting Arguments with reason = {}", reason), + #[cfg(not(feature = "alloc"))] + Error::ConflictArg => + "There were conflicting arguments. Reasons not shown without `alloc` feature.", + } + ) } } From 333c9924e681a890594113e3aff25f841bcd505f Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Sat, 26 Apr 2025 17:39:53 +0800 Subject: [PATCH 06/13] Add error messages arising from ndarray_conv --- sci-rs-core/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sci-rs-core/src/lib.rs b/sci-rs-core/src/lib.rs index 8de2ed82..83fb565f 100644 --- a/sci-rs-core/src/lib.rs +++ b/sci-rs-core/src/lib.rs @@ -34,6 +34,12 @@ pub enum Error { /// Two or more optional arguments passed into functions conflict. #[cfg(not(feature = "alloc"))] ConflictArg, + /// Errors raised by [ndarray_conv::Error] + #[cfg(feature = "alloc")] + Conv { reason: alloc::string::String }, + /// Errors raised by [ndarray_conv::Error] + #[cfg(not(feature = "alloc"))] + Conv, } impl fmt::Display for Error { @@ -54,6 +60,13 @@ impl fmt::Display for Error { #[cfg(not(feature = "alloc"))] Error::ConflictArg => "There were conflicting arguments. Reasons not shown without `alloc` feature.", + #[cfg(feature = "alloc")] + Error::Conv { reason } => format!( + "An error occurred during the convolution from ndarray_conv with reason {}.", + reason + ), + #[cfg(not(feature = "alloc"))] + Error::Conv => "An error occurred during the convolution from ndarray_conv. Reasons not shown without `alloc` feature.", } ) } From bfcbc37e92ba3961896481ff2308705311e15ad4 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Sun, 27 Apr 2025 19:10:39 +0800 Subject: [PATCH 07/13] Add linear convolution through ndarray_conv in num-rs space There will be other functions in sci-rs::signal space that uses the linear convolution. --- sci-rs-core/Cargo.toml | 1 + sci-rs-core/src/lib.rs | 2 + sci-rs-core/src/num_rs/convolve/mod.rs | 128 +++++++++++++++++++++++++ sci-rs-core/src/num_rs/mod.rs | 2 + 4 files changed, 133 insertions(+) diff --git a/sci-rs-core/Cargo.toml b/sci-rs-core/Cargo.toml index 8ff58964..2a1e814e 100644 --- a/sci-rs-core/Cargo.toml +++ b/sci-rs-core/Cargo.toml @@ -27,3 +27,4 @@ std = ['alloc'] [dependencies] ndarray = { version = "0.16.1", default-features = false } ndarray-conv = { version = "0.4.1" } +num-traits = { version = "0.2.15", default-features = false } diff --git a/sci-rs-core/src/lib.rs b/sci-rs-core/src/lib.rs index 83fb565f..f52e09a4 100644 --- a/sci-rs-core/src/lib.rs +++ b/sci-rs-core/src/lib.rs @@ -74,4 +74,6 @@ impl fmt::Display for Error { impl error::Error for Error {} +/// Collection of numpy-like functions for use by sci-rs. +/// Provide behaviour parity against Numpy, even if the types are not identical. pub mod num_rs; diff --git a/sci-rs-core/src/num_rs/convolve/mod.rs b/sci-rs-core/src/num_rs/convolve/mod.rs index e927424e..78da2f06 100644 --- a/sci-rs-core/src/num_rs/convolve/mod.rs +++ b/sci-rs-core/src/num_rs/convolve/mod.rs @@ -1,5 +1,10 @@ mod ndarray_conv_binds; +use crate::{Error, Result}; +use alloc::string::ToString; +use ndarray::{Array1, ArrayView1}; +use ndarray_conv::{ConvExt, PaddingMode}; + /// Convolution mode determines behavior near edges and output size pub enum ConvolveMode { /// Full convolution, output size is `in1.len() + in2.len() - 1` @@ -9,3 +14,126 @@ pub enum ConvolveMode { /// Same convolution, output size is `in1.len()` Same, } + +/// Best effort parallel behaviour with numpy's convolve method. We take `v` as the convolution +/// kernel. +/// +/// Returns the discrete, linear convolution of two one-dimensional sequences. +/// +/// # Parameters +/// * `a` : (N,) [[array_like]]([ndarray::Array1]) +/// Signal to be (linearly) convolved. +/// * `v` : (M,) [[array_like]]([ndarray::Array1]) +/// Second one-dimensional input array. Convolution kernel by reference. +/// * `mode` : [ConvolveMode] +/// [ConvolveMode::Full]: +/// By default, mode is 'full'. This returns the convolution at each point of overlap, with an +/// output shape of (N+M-1,). At the end-points of the convolution, the signals do not overlap +/// completely, and boundary effects may be seen. +/// +/// [ConvolveMode::Same]: +/// Mode 'same' returns output of length ``max(M, N)``. Boundary effects are still visible. +/// +/// [ConvolveMode::Valid]: +/// Mode 'valid' returns output of length ``max(M, N) - min(M, N) + 1``. The convolution +/// product is only given for points where the signals overlap completely. Values outside the +/// signal boundary have no effect. +/// +/// # Panics +/// We assume that `v` is shorter than `a`. +/// +/// # Examples +/// With [ConvolveMode::Full]: +/// ``` +/// use ndarray::array; +/// use sci_rs_core::num_rs::{ConvolveMode, convolve}; +/// +/// let a = array![1., 2., 3.]; +/// let v = array![0., 1., 0.5]; +/// +/// let expected = array![0., 1., 2.5, 4., 1.5]; +/// let result = convolve(a, (&v).into(), ConvolveMode::Full).unwrap(); +/// assert_eq!(result, expected); +/// ``` +/// With [ConvolveMode::Same]: +/// ``` +/// use ndarray::array; +/// use sci_rs_core::num_rs::{ConvolveMode, convolve}; +/// +/// let a = array![1., 2., 3.]; +/// let v = array![0., 1., 0.5]; +/// +/// let expected = array![1., 2.5, 4.]; +/// let result = convolve(a, (&v).into(), ConvolveMode::Same).unwrap(); +/// assert_eq!(result, expected); +/// ``` +/// With [ConvolveMode::Same]: +/// ``` +/// use ndarray::array; +/// use sci_rs_core::num_rs::{ConvolveMode, convolve}; +/// +/// let a = array![1., 2., 3.]; +/// let v = array![0., 1., 0.5]; +/// +/// let expected = array![2.5]; +/// let result = convolve(a, (&v).into(), ConvolveMode::Valid).unwrap(); +/// assert_eq!(result, expected); +/// ``` +pub fn convolve(a: Array1, v: ArrayView1, mode: ConvolveMode) -> Result> +where + T: num_traits::NumAssign + core::marker::Copy + core::fmt::Debug, +{ + // Treat v as the convolution kernel. + debug_assert!(v.len() <= a.len()); + + // Flip the convolution kernel (see [ndarray_conv#6](https://github.com/TYPEmber/ndarray-conv/issues/6)) + // waiting for ndarray_conv v0.4.2 to not require for us to flip + let v: Array1<_> = { + let mut v = v.to_vec(); + v.reverse(); + v.into() + }; + + // Convolve + a.conv(&v, mode.into(), PaddingMode::Zeros) + .map_err(|e| Error::Conv { + reason: e.to_string(), + }) +} + +#[cfg(test)] +mod linear_convolve { + use super::*; + use alloc::vec; + use ndarray::array; + + #[test] + fn full() { + let a = array![1., 2., 3.]; + let v = array![0., 1., 0.5]; + + let expected = array![0., 1., 2.5, 4., 1.5]; + let result = convolve(a, (&v).into(), ConvolveMode::Full).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn same() { + let a = array![1., 2., 3.]; + let v = array![0., 1., 0.5]; + + let expected = array![1., 2.5, 4.]; + let result = convolve(a, (&v).into(), ConvolveMode::Same).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn valid() { + let a = array![1., 2., 3.]; + let v = array![0., 1., 0.5]; + + let expected = array![2.5]; + let result = convolve(a, (&v).into(), ConvolveMode::Valid).unwrap(); + assert_eq!(result, expected); + } +} diff --git a/sci-rs-core/src/num_rs/mod.rs b/sci-rs-core/src/num_rs/mod.rs index 424dc967..e6e9679c 100644 --- a/sci-rs-core/src/num_rs/mod.rs +++ b/sci-rs-core/src/num_rs/mod.rs @@ -1,2 +1,4 @@ +#[cfg(feature = "alloc")] mod convolve; +#[cfg(feature = "alloc")] pub use convolve::*; From ea43d53495ee1c0ae692749f8b738895b7919096 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:42:05 +0800 Subject: [PATCH 08/13] Change np-convolve to use ArrayView instead of ArrayBase in the 1st arg One can use into the Make and ArrayView from Array but not the other way round, this makes the function signature abit more friendly without needing to use `.to_owned()` needlessly. --- sci-rs-core/src/num_rs/convolve/mod.rs | 28 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/sci-rs-core/src/num_rs/convolve/mod.rs b/sci-rs-core/src/num_rs/convolve/mod.rs index 78da2f06..a4f7a79b 100644 --- a/sci-rs-core/src/num_rs/convolve/mod.rs +++ b/sci-rs-core/src/num_rs/convolve/mod.rs @@ -24,7 +24,7 @@ pub enum ConvolveMode { /// * `a` : (N,) [[array_like]]([ndarray::Array1]) /// Signal to be (linearly) convolved. /// * `v` : (M,) [[array_like]]([ndarray::Array1]) -/// Second one-dimensional input array. Convolution kernel by reference. +/// Second one-dimensional input array. /// * `mode` : [ConvolveMode] /// [ConvolveMode::Full]: /// By default, mode is 'full'. This returns the convolution at each point of overlap, with an @@ -52,7 +52,7 @@ pub enum ConvolveMode { /// let v = array![0., 1., 0.5]; /// /// let expected = array![0., 1., 2.5, 4., 1.5]; -/// let result = convolve(a, (&v).into(), ConvolveMode::Full).unwrap(); +/// let result = convolve((&a).into(), (&v).into(), ConvolveMode::Full).unwrap(); /// assert_eq!(result, expected); /// ``` /// With [ConvolveMode::Same]: @@ -64,7 +64,7 @@ pub enum ConvolveMode { /// let v = array![0., 1., 0.5]; /// /// let expected = array![1., 2.5, 4.]; -/// let result = convolve(a, (&v).into(), ConvolveMode::Same).unwrap(); +/// let result = convolve((&a).into(), (&v).into(), ConvolveMode::Same).unwrap(); /// assert_eq!(result, expected); /// ``` /// With [ConvolveMode::Same]: @@ -76,11 +76,12 @@ pub enum ConvolveMode { /// let v = array![0., 1., 0.5]; /// /// let expected = array![2.5]; -/// let result = convolve(a, (&v).into(), ConvolveMode::Valid).unwrap(); +/// let result = convolve((&a).into(), (&v).into(), ConvolveMode::Valid).unwrap(); /// assert_eq!(result, expected); /// ``` -pub fn convolve(a: Array1, v: ArrayView1, mode: ConvolveMode) -> Result> +pub fn convolve(a: ArrayView1, v: ArrayView1, mode: ConvolveMode) -> Result> where + // ? Debug for ndarray_conv::ConvExt::conv T: num_traits::NumAssign + core::marker::Copy + core::fmt::Debug, { // Treat v as the convolution kernel. @@ -95,10 +96,17 @@ where }; // Convolve - a.conv(&v, mode.into(), PaddingMode::Zeros) - .map_err(|e| Error::Conv { + let result = a.conv(&v, mode.into(), PaddingMode::Zeros); + #[cfg(feature = "alloc")] + { + result.map_err(|e| Error::Conv { reason: e.to_string(), }) + } + #[cfg(not(feature = "alloc"))] + { + result.map_err({ Error::Conv }) + } } #[cfg(test)] @@ -113,7 +121,7 @@ mod linear_convolve { let v = array![0., 1., 0.5]; let expected = array![0., 1., 2.5, 4., 1.5]; - let result = convolve(a, (&v).into(), ConvolveMode::Full).unwrap(); + let result = convolve((&a).into(), (&v).into(), ConvolveMode::Full).unwrap(); assert_eq!(result, expected); } @@ -123,7 +131,7 @@ mod linear_convolve { let v = array![0., 1., 0.5]; let expected = array![1., 2.5, 4.]; - let result = convolve(a, (&v).into(), ConvolveMode::Same).unwrap(); + let result = convolve((&a).into(), (&v).into(), ConvolveMode::Same).unwrap(); assert_eq!(result, expected); } @@ -133,7 +141,7 @@ mod linear_convolve { let v = array![0., 1., 0.5]; let expected = array![2.5]; - let result = convolve(a, (&v).into(), ConvolveMode::Valid).unwrap(); + let result = convolve((&a).into(), (&v).into(), ConvolveMode::Valid).unwrap(); assert_eq!(result, expected); } } From aee705df96c3a3b21504bfdc30aa18094ed9a38e Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:53:09 +0800 Subject: [PATCH 09/13] Remove kernel vs signal debug assertion There is nothing distinguishing between kernel and signal with both arguments now being identical in type. --- sci-rs-core/src/num_rs/convolve/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/sci-rs-core/src/num_rs/convolve/mod.rs b/sci-rs-core/src/num_rs/convolve/mod.rs index a4f7a79b..9a9dc213 100644 --- a/sci-rs-core/src/num_rs/convolve/mod.rs +++ b/sci-rs-core/src/num_rs/convolve/mod.rs @@ -84,9 +84,6 @@ where // ? Debug for ndarray_conv::ConvExt::conv T: num_traits::NumAssign + core::marker::Copy + core::fmt::Debug, { - // Treat v as the convolution kernel. - debug_assert!(v.len() <= a.len()); - // Flip the convolution kernel (see [ndarray_conv#6](https://github.com/TYPEmber/ndarray-conv/issues/6)) // waiting for ndarray_conv v0.4.2 to not require for us to flip let v: Array1<_> = { From 1b484a060ad397f578d135841d678a0c0aeaff6b Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Tue, 29 Apr 2025 12:30:44 +0800 Subject: [PATCH 10/13] Add sci-rs-core Cargo.toml --- sci-rs-core/Cargo.toml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 sci-rs-core/Cargo.toml diff --git a/sci-rs-core/Cargo.toml b/sci-rs-core/Cargo.toml new file mode 100644 index 00000000..2a1e814e --- /dev/null +++ b/sci-rs-core/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "sci-rs-core" +version = "0.0.0" +edition = "2021" +authors = ["Jacob Trueb "] +description = "Core library for sci-rs internals." +license = "MIT OR Apache-2.0" +repository = "https://github.com/qsib-cbie/sci-rs.git" +homepage = "https://github.com/qsib-cbie/sci-rs.git" +readme = "../README.md" +keywords = ["scipy", "dsp", "signal", "filter", "design"] +categories = ["science", "mathematics", "no-std", "embedded"] + + +[package.metadata.docs.rs] +all-features = true + +[features] +default = ['alloc'] + +# Allow allocating vecs, matrices, etc. +alloc = [] + +# Enable FFT and standard library features +std = ['alloc'] + +[dependencies] +ndarray = { version = "0.16.1", default-features = false } +ndarray-conv = { version = "0.4.1" } +num-traits = { version = "0.2.15", default-features = false } From fc251347dc02c3913a4388d211c1b135c8af0730 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Tue, 29 Apr 2025 12:38:34 +0800 Subject: [PATCH 11/13] Build sci-rs-core with features used to build sci-rs --- sci-rs/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sci-rs/Cargo.toml b/sci-rs/Cargo.toml index 1269f678..99647819 100644 --- a/sci-rs/Cargo.toml +++ b/sci-rs/Cargo.toml @@ -19,10 +19,10 @@ all-features = true default = ['alloc'] # Allow allocating vecs, matrices, etc. -alloc = ['nalgebra/alloc', 'nalgebra/libm', 'kalmanfilt/alloc'] +alloc = ['nalgebra/alloc', 'nalgebra/libm', 'kalmanfilt/alloc', 'sci-rs-core/alloc'] # Enable FFT and standard library features -std = ['nalgebra/std', 'nalgebra/macros', 'rustfft', 'alloc'] +std = ['nalgebra/std', 'nalgebra/macros', 'rustfft', 'alloc','sci-rs-core/std'] # Enable debug plotting through python system calls plot = ['std'] @@ -36,7 +36,7 @@ lstsq = { version = "0.6.0", default-features = false } rustfft = { version = "6.2.0", optional = true } kalmanfilt = { version = "0.3.0", default-features = false } gaussfilt = { version = "0.1.3", default-features = false } -sci-rs-core = { path = "../sci-rs-core" } +sci-rs-core = { path = "../sci-rs-core", default-features = false } [dev-dependencies] approx = "0.5.1" From f21a6bcdfe7f45304083821e52dffe9a1789e5c2 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:05:59 +0800 Subject: [PATCH 12/13] Update ndarray-conv to 0.5.0 in core This version of ndarray-conv accounts for both cross-correlation and convolution. --- sci-rs-core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sci-rs-core/Cargo.toml b/sci-rs-core/Cargo.toml index 2a1e814e..26fe7017 100644 --- a/sci-rs-core/Cargo.toml +++ b/sci-rs-core/Cargo.toml @@ -26,5 +26,5 @@ std = ['alloc'] [dependencies] ndarray = { version = "0.16.1", default-features = false } -ndarray-conv = { version = "0.4.1" } +ndarray-conv = { version = "0.5.0" } num-traits = { version = "0.2.15", default-features = false } From 182658d3fe37d436a4f9ebb64213e9a97464bd46 Mon Sep 17 00:00:00 2001 From: SpookyYomo <48710653+SpookyYomo@users.noreply.github.com> Date: Mon, 14 Jul 2025 22:17:17 +0800 Subject: [PATCH 13/13] Fix linear convolve behaviour Behavior of convolve in ndarray-conv was changed with 0.5.0. --- sci-rs-core/src/num_rs/convolve/mod.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sci-rs-core/src/num_rs/convolve/mod.rs b/sci-rs-core/src/num_rs/convolve/mod.rs index 9a9dc213..2d80c7f7 100644 --- a/sci-rs-core/src/num_rs/convolve/mod.rs +++ b/sci-rs-core/src/num_rs/convolve/mod.rs @@ -81,17 +81,8 @@ pub enum ConvolveMode { /// ``` pub fn convolve(a: ArrayView1, v: ArrayView1, mode: ConvolveMode) -> Result> where - // ? Debug for ndarray_conv::ConvExt::conv - T: num_traits::NumAssign + core::marker::Copy + core::fmt::Debug, + T: num_traits::NumAssign + core::marker::Copy, { - // Flip the convolution kernel (see [ndarray_conv#6](https://github.com/TYPEmber/ndarray-conv/issues/6)) - // waiting for ndarray_conv v0.4.2 to not require for us to flip - let v: Array1<_> = { - let mut v = v.to_vec(); - v.reverse(); - v.into() - }; - // Convolve let result = a.conv(&v, mode.into(), PaddingMode::Zeros); #[cfg(feature = "alloc")]