32个版本

0.3.15 2024年7月26日
0.3.13 2024年5月8日
0.3.12 2024年3月5日
0.3.7 2023年12月13日
0.1.7 2022年11月9日

#55 in 数学

Download history 405/week @ 2024-05-03 185/week @ 2024-05-10 212/week @ 2024-05-17 106/week @ 2024-05-24 24/week @ 2024-05-31 134/week @ 2024-06-07 75/week @ 2024-06-14 71/week @ 2024-06-21 23/week @ 2024-06-28 83/week @ 2024-07-05 31/week @ 2024-07-12 44/week @ 2024-07-19 313/week @ 2024-07-26 450/week @ 2024-08-02 852/week @ 2024-08-09 467/week @ 2024-08-16

2,086 每月下载量
komunikilo中使用

MIT/Apache

175KB
3.5K SLoC

CI Crate Crate Downloads

sci-rs

SciPy的Rust实现,主要用于在嵌入式no_std + alloc、移动(iOS)和服务器上使用,以将后处理学术研究转换为可移植、实时部署。

内存使用是重点。虽然alloc是默认功能,但库将优先考虑不进行运行时分配的实现。分配函数使用*_dyn约定,而不分配函数使用*_st约定。

功能

  • stats
    • 均值、中位数、众数、方差、标准差、z分数、自相关
  • IIR滤波器设计
    • ba, zpk, 或 sos格式
    • Butterworth
      • 低通、高通、带通、带阻
  • IIR sosfilt, sosfiltfilt
  • GH, GHK滤波(alpha-beta,alpha-beta-gamma)
  • 卡尔曼滤波
  • 高斯滤波设计及滤波
  • Savitsky-Golay设计及滤波
  • 重采样(使用傅里叶方法)

Butterworth(低/高/带通)滤波器设计到SOS或BA。使用sosfilt和sosfiltfilt进行SOS滤波。标准差或中位数等统计功能。

use sci_rs::signal::filter::{design::*, sosfiltfilt_dyn};

// MATLAB style function to generate Section order Section
pub fn butter_filter_lowpass<F>(order: usize, lowcut: F, fs: F) -> Vec<Sos<F>>
where
    F: Float + RealField + Sum,
{
    // Design Second Order Section (SOS) filter
    let filter = butter_dyn(
        order,
        [lowcut].to_vec(),
        Some(FilterBandType::Lowpass),
        Some(false),
        Some(FilterOutputType::Sos),
        Some(fs),
    );
    let DigitalFilter::Sos(SosFormatFilter {sos}) = filter else {
        panic!("Failed to design filter");
    };
    sos
}

// Or call iirfilter directly to design
let filter = iirfilter_dyn::<f32>(
    4,
    vec![10., 50.],
    None,
    None,
    Some(FilterBandType::Bandpass),
    Some(FilterType::Butterworth),
    Some(false),
    Some(FilterOutputType::Sos),
    Some(1666.),
);
let DigitalFilter::Sos(sos) = filter else { panic!("Not SOS filter") };
let data = (0..100000).map(|i| i as f32);
let filtered: Vec<f32> = sosfiltfilt_dyn(data, &sos.sos);

根据SciPy测试通过

为了保证正确性,测试使用来自SciPy的硬编码魔法数字。目前,这需要手动编写脚本和测试。建议为开发此包使用本地Python虚拟环境。

贡献

如果实现或纠正新功能,请将您的更改与SciPy的正确性进行比较。

生成绘图并将其添加到PR可以帮助加速调试和审批。

以下是一个快速绘图脚本

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np

# Some data from scipy
a = np.arange(10)
# Some data from sci-rs
b = np.arange(10) * 2

# A plot with two rows
fig = plt.figure(figsize=(10, 10))
gs = gridspec.GridSpec(2, 1)
ax = fig.add_subplot(gs[0, 0])

# A plot with titles and labels
ax.plot(a, label="Series a")
ax.plot(b, label="Series b")
ax.set_title('Filter Comparison')
ax.set_ylabel('Magnitude (a.u.)')
ax.set_xlabel('Time (s)')
ax.legend()

# Another plot with titles and labels using the same axis
ax = fig.add_subplot(gs[1, 0], sharex=ax)
ax.plot(a - b, label = "Series Delta")
ax.set_ylabel('Magnitude Difference (a.u.)')
ax.set_xlabel('Time (s)')
ax.legend()

# Manually inspect the data and grab screenshot
plt.tight_layout()
plt.show()

Example Plot

MSRV

在此项目成熟之前,我们将致力于支持Rust的最新稳定版本。

一些不稳定的功能,例如 const-generic-exprs,可以启用更多非分配支持,但不可靠。https://github.com/rust-lang/rust/issues/106423 在 nightly 上已损坏超过一年。

使用 *_st 接口的不稳定功能使用已被移除,直至另行通知。

未来目标

Python :: 能够用 sciprs 替换 scipy

以下是一个使用 sosfiltfilt 对 numpy 数组进行带通滤波的 Python 示例

import numpy as np
# from scipy.signal import butter, sosfiltfilt
from sciprs.signal import butter, sosfiltfilt

raw = np.array([0.0, 0.09414586007215595, 0.18745540640340155, ...])
sos = butter(4, [10, 50], btype='bandpass', output='sos', fs=1666)
np_ndarray_filtered_by_rust_code = sosfiltfilt(buttersos, raw, 0)

Rust :: 使用宏复制粘贴 Python

为了最小化在 Rust 中直接使用 scipy Python 代码时的摩擦,sci-rs 将包括一个宏系统,用于以与 Python 兼容的语法扩展当前与 pyo3 和本地 Python 运行时交互的 sci-rs 逻辑。以下内容尚不存在,但为未来的兼容性提供了愿景。

let filtered_data: nalgebra::DVector<f64> = sciprs! {
    raw = np.array([0.0, 0.09414586007215595, 0.18745540640340155, 0.27909975437050305, 0.3682648115914595])
    sos = butter(4, [10, 50], btype='bandpass', output='sos', fs=1666)
    sosfiltfilt(sos, raw, 0)
};

系统线性代数依赖

我还没有决定我们是否会主要使用 ndarray 或 nalgebra。无论是哪种抽象,都可以通过 Python 运行时将其转换为 numpy ndarray。在这两个 crate 之间,创建 sci-rs 的一个主要原因是支持可能无法合理访问 LAPACK 或 blas 的目标。库的选择将基于功能兼容性而不是性能。话虽如此,在许多情况下,性能将非常出色,并且会有基准测试。

ndarray 和 nalgebra 都具有与矩阵乘法和 lapack 相关的功能。Nalgebra 的优点是可以在 no_std 中非常实用。

尽可能使用迭代器和切片模式以提高兼容性。在未来,rayon 可能会成为并行计算的候选者。

Maturin + Scipy + Sciprs + Numpy + Matplotlib

一旦在本地实现了方法,您的本地 sciprs crate 可以指向其依赖项,该依赖项位于您的本地 sci-rs 目录中 sci-rs = { path = "../sci-rs" }。您可以使用 maturin develop 来构建和安装 Rust 中所做的更改,并通过 Python 直接进行脚本比较。

(sciprs-env) jwtrueb@macbook-pro sciprs % maturin develop --release
🔗 Found pyo3 bindings
🐍 Found CPython 3.9 at /Users/jwtrueb/Desktop/workspace/sciprs/sciprs-env/bin/python
   Compiling autocfg v1.1.0
   Compiling target-lexicon v0.12.4
...
   Compiling nalgebra v0.31.1
   Compiling numpy v0.16.2
   Compiling sci-rs v0.1.2 (/Users/jwtrueb/Desktop/workspace/sci-rs/sci-rs)
   Compiling nalgebra-numpy v0.3.0 (https://github.com/qsib-cbie/nalgebra-numpy#427a8135)
   Compiling sciprs v0.1.0 (/Users/jwtrueb/Desktop/workspace/sciprs)
    Finished release [optimized] target(s) in 12.10s
📦 Built wheel for CPython 3.9 to /var/folders/n_/s24_t4gj6rnf7r2kxj376df40000gn/T/.tmphMfGAv/sciprs-0.1.0-cp39-cp39-macosx_11_0_arm64.whl

importlib.reload 不起作用,因此每次都需要重新启动解释器。

# load the module with the new Rust code
import sciprs

# do what you want
print(sciprs.call_new_function(1,2,3))

依赖关系

~5.5MB
~114K SLoC