探索 Rust 的 NumPy:介绍与库推荐
引言:为何在 Rust 中寻找 NumPy?
Python 凭借其简洁的语法和庞大的科学计算生态系统,在数据科学、机器学习以及数值计算领域占据了主导地位。NumPy (Numerical Python) 是这个生态系统的基石之一。它提供了一个功能强大的 N 维数组对象(ndarray
),以及大量用于数组操作的函数,包括线性代数、傅立叶变换、随机数生成等。NumPy 的核心操作通常在 C 或 Fortran 中实现,这使得 Python 能够以接近原生代码的速度执行复杂的数值计算。
然而,尽管 Python 及其生态系统非常强大,但在某些场景下,开发者可能会寻求其他选择:
- 性能极限: 对于计算密集型任务,尽管 NumPy 内部很快,但 Python 解释器的全局解释器锁(GIL)以及动态类型带来的开销,可能会限制多核并行或某些底层优化。
- 系统级应用: 开发需要直接与操作系统交互、对内存布局有精细控制、或需要生成独立二进制文件的应用时,Python 可能不是最佳选择。
- 内存安全: Python 是一种垃圾回收语言,虽然使用方便,但在对内存安全要求极高的场景(如嵌入式系统、高性能服务器)中,难以提供 Rust 那样的编译时保证。
- 集成与部署: 将 Python 代码集成到其他语言开发的大型系统中,或将其部署为独立的、高性能的服务,有时会遇到复杂性。
这时,Rust 进入了视野。Rust 是一种专注于安全、并发和性能的系统编程语言。它提供零成本抽象、强大的类型系统、严格的所有权模型,能够在编译时捕获内存安全错误,并且不依赖垃圾回收。这些特性使得 Rust 非常适合开发高性能、可靠的底层库或独立应用。
那么,对于习惯了 NumPy 便捷性的开发者来说,如何在 Rust 中进行高效的数值计算呢?Rust 是否有“自己的 NumPy”?答案是:Rust 目前没有一个完全等同于 NumPy 的单一、庞大、集所有功能于一身的库。相反,Rust 的生态系统提供了几个专注于不同方面、但同样功能强大且高效的库,它们共同构成了 Rust 的数值计算工具箱。
本文将深入探讨 Rust 中用于数值计算的主要库,重点介绍它们的功能、特点以及如何使用,帮助您在 Rust 中构建高性能的数值应用。
Rust 进行数值计算的优势与挑战
在深入库介绍之前,我们先来理解在 Rust 中进行数值计算的优势和面临的挑战。
优势:
- 极致性能: Rust 的零成本抽象意味着您可以编写与 C/C++ 媲美的代码,而无需担心运行时开销。内存布局可以被精确控制,结合现代 CPU 的特性(如 SIMD 指令),可以实现非常高的计算效率。
- 内存安全与并发: Rust 的所有权系统和类型系统在编译时保证了内存安全和数据竞争的避免。这使得在编写并行数值算法时,可以更加自信,减少运行时错误。
rayon
等库可以轻松地将计算密集型任务并行化。 - 无 GC 开销: Rust 不使用垃圾回收,这意味着在进行大量数值计算和内存分配时,不会有 GC 暂停导致的延迟或不可预测的性能抖动。
- 类型系统增强: Rust 的类型系统可以用于编码关于数组形状、维度等信息,尽管不像某些学术语言那样常见,但特定库(如
nalgebra
的固定大小矩阵)可以利用类型系统提供额外的安全性和编译时优化。 - 可移植性与部署: Rust 代码可以编译成独立的二进制文件,易于分发和部署,无需外部运行时依赖(除了 libc)。它可以编译到各种平台,包括 WebAssembly。
挑战:
- 生态系统成熟度: 相比 Python 的 NumPy 及其围绕构建的 SciPy, pandas, matplotlib 等庞大生态,Rust 的数值计算库生态相对年轻,功能仍在不断完善中。某些特定领域的算法实现可能不如 SciPy 全面。
- 学习曲线: Rust 本身具有较高的学习曲线,尤其是所有权、借用、生命周期等概念。理解并正确使用数值计算库(特别是涉及视图、所有权转移时)需要一定的学习。
- 库分散: 如前所述,NumPy 的功能在 Rust 中被分到不同的库中(数组操作、线性代数、FFT 等),开发者需要了解并组合使用这些库。
- 语法冗长: 相比 Python 的简洁,Rust 的语法在表达某些数组操作时可能显得更冗长,尽管宏和trait可以缓解一部分问题。
- 动态维度: 处理编译时未知维度的数组 (
ndarray
的主要用例) 需要动态大小类型和运行时检查,虽然库本身提供了很好的抽象,但与 Python 的动态性相比,仍然需要更显式的处理。
了解了这些,我们现在来看看 Rust 中主要的数值计算库。
Rust 的 NumPy 等效库:主要推荐
Rust 中用于处理 N 维数组和执行数值计算的最核心库是 ndarray
。此外,还有专注于线性代数的 nalgebra
和 faer
,以及处理特定任务(如 FFT)的库。
1. ndarray
: Rust 中的 N 维数组基石
如果要在 Rust 中找到与 NumPy ndarray
最接近的对应物,那无疑是 ndarray
crate。它提供了一个灵活、高效的 N 维数组类型 Array
和 ArrayView
/ArrayViewMut
。
核心概念:
Array<A, D>
: 拥有数据的 N 维数组。A
是元素类型,D
是维度类型(如Ix1
,Ix2
,Ix3
,IxDyn
)。ArrayView<'a, A, D>
: 对现有数组的不可变引用视图。类似于 NumPy 的切片,它不拥有数据,只是提供了访问原始数据的接口。这使得切片、转置、reshape 等操作非常高效,因为它们不涉及数据复制。ArrayViewMut<'a, A, D>
: 对现有数组的可变引用视图。允许就地修改数据。- 维度类型 (
D
):Ix1
到Ix6
代表固定维度的数组,维度在编译时已知。IxDyn
代表动态维度的数组,维度在运行时确定。这提供了编译时安全性和运行时灵活性的选择。 - 布局 (Layout):
ndarray
支持行主序 (C-order) 和列主序 (Fortran-order) 的数组,以及具有任意步长 (strides) 的数组视图。这对于与外部库(如 BLAS/LAPACK)交互或处理特定数据格式非常重要。
基本用法与特性:
-
创建数组:
“`rust
use ndarray::{array, Array, Ix2, s};// 使用宏创建固定维度的数组
let a: Array= array![[1., 2.], [3., 4.]];
println!(“a:\n{}”, a);// 创建全零或全一数组
let zeros = Array::::zeros((3, 4));
println!(“zeros:\n{}”, zeros);// 创建特定形状的数组,并初始化
let mut arr = Array::::zeros((2, 3));
arr[[0, 1]] = 5.0;
println!(“arr:\n{}”, arr);// 使用 arange
use ndarray::Array1;
let arange_arr: Array1= Array1::arange(10); // [0, 1, …, 9]
println!(“arange_arr: {}”, arange_arr);// 从 Vec 或 slice 创建动态维度数组
let data = vec![1, 2, 3, 4, 5, 6];
let dynamic_arr = Array::from_shape_vec((2, 3), data).unwrap();
println!(“dynamic_arr:\n{}”, dynamic_arr);
“` -
索引与切片:
ndarray
的索引使用方括号[]
,可以传入元组或宏s![]
进行切片。切片操作返回的是视图,非常高效。
“`rust
let arr = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]];// 元素访问
println!(“arr[1, 2]: {}”, arr[[1, 2]]); // Output: 6// 行切片 (视图)
let row_view = arr.row(0);
println!(“row_view: {}”, row_view); // Output: [1, 2, 3]// 列切片 (视图)
let col_view = arr.column(1);
println!(“col_view: {}”, col_view); // Output: [2, 5, 8]// 使用 s![] 进行多维切片
// arr[0:2, 1:3]
let slice_view = arr.slice(s![0..2, 1..3]);
println!(“slice_view:\n{}”, slice_view);
/ Output:
[[2, 3],
[5, 6]]
/// 步长切片 arr[::2, ::2]
let stepped_slice = arr.slice(s![..;2, ..;2]);
println!(“stepped_slice:\n{}”, stepped_slice);
/ Output:
[[1, 3],
[7, 9]]
/// 使用 .select() 按索引选择行或列
let selected_rows = arr.select(ndarray::Axis(0), &[0, 2]);
println!(“selected_rows:\n{}”, selected_rows);
/ Output:
[[1, 2, 3],
[7, 8, 9]]
/
``
s![]
请注意,宏的语法
start..end;step与 Python 的
start:end:step` 非常相似。 -
元素级操作:
ndarray
重载了大部分算术运算符(+
,-
,*
,/
),允许直接对数组进行元素级操作。这些操作会自动处理广播 (Broadcasting),规则类似于 NumPy。
“`rust
let a = array![[1., 2.], [3., 4.]];
let b = array![[5., 6.], [7., 8.]];// 数组加法 (元素级)
let c = a + b;
println!(“a + b:\n{}”, c);// 数组与标量相乘
let d = a * 2.0;
println!(“a * 2.0:\n{}”, d);// 广播示例
let row_vec = array![10., 20.]; // Shape (2,)
let broadcast_add = a + row_vec; // row_vec is broadcast to [[10, 20], [10, 20]]
println!(“broadcast_add:\n{}”, broadcast_add);
``
ndarray` 通过 Traits 实现这些操作,提供了类型安全。 -
聚合方法:
ndarray
提供了许多类似于 NumPy 的聚合方法,如sum()
,mean()
,max()
,min()
,std()
,var()
等。可以在整个数组上执行,也可以指定轴 (Axis)。
“`rust
let arr = array![[1., 2., 3.], [4., 5., 6.]]; // Shape (2, 3)println!(“Sum of all elements: {}”, arr.sum()); // Output: 21.0
// Sum along axis 0 (columns)
let sum_axis0 = arr.sum_axis(ndarray::Axis(0));
println!(“Sum along axis 0: {}”, sum_axis0); // Output: [5.0, 7.0, 9.0] (Shape (3,))// Mean along axis 1 (rows)
let mean_axis1 = arr.mean_axis(ndarray::Axis(1));
println!(“Mean along axis 1: {}”, mean_axis1.unwrap()); // Output: [2.0, 5.0] (Shape (2,))
“` -
矩阵乘法: 使用
@
运算符进行矩阵乘法(需要启用matrixmultiply
feature,通常默认开启)。
“`rust
let a = array![[1., 2.], [3., 4.]]; // 2×2
let b = array![[5., 6.], [7., 8.]]; // 2×2let c = a.dot(&b); // Or a @ b;
println!(“a @ b:\n{}”, c);
/ Output:
[[19, 22],
[43, 50]]
/let vec = array![1., 2.]; // 1D array, treated as row vector for multiplication
let result = vec.dot(&a); // Or vec @ a;
println!(“vec @ a: {}”, result); // Output: [7.0, 10.0]
“` -
视图操作:
to_owned()
,into_owned()
用于将视图转换为拥有数据的数组。t()
用于转置视图。reversed_axes()
等。
“`rust
let arr = array![[1, 2], [3, 4]];
let transposed_view = arr.t();
println!(“Transposed view:\n{}”, transposed_view);// To get an owned, transposed array:
let owned_transposed = arr.t().to_owned();
println!(“Owned transposed:\n{}”, owned_transposed);
“` -
并行计算:
ndarray
与rayon
crate 集成良好,可以方便地并行化许多操作,如迭代、map、fold 等。只需引入use rayon::prelude::*;
并在ndarray
迭代器上使用par_iter()
或par_iter_mut()
。
“`rust
use rayon::prelude::*;
use ndarray::Array2;let mut arr = Array2::
::zeros((100, 100)); // Parallel iteration and modification
arr.par_iter_mut()
.enumerate()
.for_each(|(i, x)| {
*x = i as f64 * 0.1;
});println!(“Small part of parallel filled array:\n{}”, arr.slice(s![0..5, 0..5]));
“`
与 NumPy 的相似之处:
- N 维数组 (
Array
对应ndarray
) - 视图 (
ArrayView
/ArrayViewMut
对应 NumPy views/slices) - 切片语法 (
s![]
类似于:
语法) - 广播 (Broadcasting)
- 元素级运算符重载
- 聚合方法 (
sum
,mean
等) - 矩阵乘法 (
.dot()
或@
)
与 NumPy 的不同之处:
- 类型系统: Rust 是静态类型语言,数组元素类型和维度类型在编译时确定或由类型推断。NumPy 是动态类型。
- 所有权与借用: Rust 严格区分拥有数据 (
Array
) 和借用数据 (ArrayView
/ArrayViewMut
),需要显式管理。NumPy 的视图管理相对隐式。 - 方法 vs. 函数: NumPy 许多操作是顶级函数 (
np.sum(arr)
),而ndarray
倾向于使用方法 (arr.sum()
)。 - 生态:
ndarray
专注于 N 维数组本身和基本操作。线性代数、FFT 等更高级功能通常由其他库提供(尽管ndarray
有一些基本实现或集成)。
ndarray
是 Rust 生态中进行通用 N 维数组操作的首选库,功能强大且性能优异。
2. nalgebra
: 专注于线性代数和几何
nalgebra
是 Rust 中另一个重要的数值计算库,但它更专注于线性代数和计算机图形学/几何学。它提供了用于向量、矩阵、旋转、四元数等的类型。
核心概念与特性:
- 固定大小 vs. 动态大小:
nalgebra
提供了VectorN<T, D>
和MatrixMN<T, R, C>
用于固定大小的向量和矩阵,其中D
,R
,C
是类型级别的维度参数(由typenum
crate 提供)。它也提供DVector<T>
和DMatrix<T>
用于运行时大小确定的向量和矩阵。固定大小类型可以在编译时进行更多优化,而动态大小类型更灵活。 - 线性代数操作: 提供了丰富的线性代数操作,如加法、乘法、转置、求逆、行列式、特征值、奇异值分解 (SVD) 等。
- 几何类型: 支持点、向量、旋转(旋转矩阵、四元数、欧拉角)、变换矩阵等,非常适合图形学、物理模拟等领域。
- Trait 系统: 大量使用 Traits 来定义数值行为,方便泛型编程。
基本用法与特性:
-
创建向量和矩阵:
“`rust
use nalgebra::{Vector3, Matrix2, dmatrix};// 固定大小向量
let vec1 = Vector3::new(1.0, 2.0, 3.0);
println!(“vec1: {}”, vec1);// 固定大小矩阵
let mat1 = Matrix2::new(1.0, 2.0,
3.0, 4.0);
println!(“mat1:\n{}”, mat1);// 动态大小矩阵 (类似 NumPy 的 np.array 或 np.matrix)
let dmat1 = dmatrix![1.0, 2.0;
3.0, 4.0;
5.0, 6.0]; // 3×2 matrix
println!(“dmat1:\n{}”, dmat1);// 从 Vec 创建动态向量/矩阵
use nalgebra::{DVector, DMatrix};
let data = vec![1.0, 2.0, 3.0, 4.0];
let dvec = DVector::from_vec(data.clone());
let dmat2 = DMatrix::from_row_slice(2, 2, &data); // 2×2 from row-major slice
println!(“dvec: {}”, dvec);
println!(“dmat2:\n{}”, dmat2);
“` -
索引: 使用
[]
或at()
进行索引。固定大小类型通常通过索引访问,动态大小类型使用[]
或at()
。
rust
let mat = Matrix2::new(1, 2, 3, 4);
println!("mat[0]: {}", mat[0]); // First column vector: [1, 3]
println!("mat[(1, 0)]: {}", mat[(1, 0)]); // Element at row 1, col 0: 3 -
操作符重载: 支持向量/矩阵加减乘除、标量乘除等。
“`rust
let mat1 = Matrix2::new(1, 2, 3, 4);
let mat2 = Matrix2::new(5, 6, 7, 8);
let vec1 = Vector3::new(1.0, 2.0, 3.0);println!(“mat1 + mat2:\n{}”, mat1 + mat2);
println!(“mat1 * 2:\n{}”, mat1 * 2);
println!(“mat1 * mat2:\n{}”, mat1 * mat2); // Matrix multiplication// Vector dot product
let vec2 = Vector3::new(4.0, 5.0, 6.0);
println!(“vec1.dot(&vec2): {}”, vec1.dot(&vec2));// Matrix-vector multiplication
let mat3 = DMatrix::from_row_slice(2, 3, &[1., 2., 3., 4., 5., 6.]); // 2×3
let vec3 = DVector::from_vec(vec![7., 8., 9.]); // 3×1
println!(“mat3 * vec3:\n{}”, mat3 * vec3); // Result is 2×1 vector
“` -
线性代数方法:
transpose()
,try_inverse()
,determinant()
,qr()
,svd()
等。
rust
let mat = DMatrix::from_row_slice(2, 2, &[1.0, 2.0, 3.0, 4.0]);
println!("mat:\n{}", mat);
println!("mat transposed:\n{}", mat.transpose());
println!("mat determinant: {}", mat.determinant());
println!("mat inverse:\n{}", mat.try_inverse().unwrap());
与 ndarray
的比较:
- 侧重点:
nalgebra
更专注于线性代数和几何实体(向量、矩阵、变换),对固定大小的类型支持更好,并提供了大量相关的算法。ndarray
更侧重于通用的 N 维数组操作、切片和广播。 - 维度处理:
nalgebra
使用typenum
支持编译时固定大小维度,或者使用DVector
/DMatrix
处理动态大小。ndarray
使用IxD
和IxDyn
。 - 生态:
nalgebra
围绕线性代数和几何构建,与图形学、物理相关的库集成更多。ndarray
更通用,可能与其他数据处理或统计库集成。 - 性能: 对于小型、固定大小的矩阵/向量操作,
nalgebra
的编译时优化可能带来优势。对于大型、动态大小的 N 维数组通用操作,两者都依赖底层的 BLAS/LAPACK 实现(如果可用),性能都很好,但侧重点不同。
如果你的主要任务是进行线性代数计算、处理向量和矩阵作为数学实体、或者进行几何变换,nalgebra
通常是更自然的选择。如果需要处理更高维度的通用数组、频繁进行切片和广播,ndarray
更适合。
3. faer
: 高性能线性代数库
faer
是 Rust 中一个相对较新但发展迅速的高性能线性代数库。它的目标是提供一个现代、安全且性能匹敌甚至超越现有 C/Fortran 实现(如 OpenBLAS, MKL)的库。
核心概念与特性:
- 性能至上:
faer
从头开始设计,利用 Rust 的特性和现代 CPU 架构(如 SIMD)来实现极致性能。它提供了自己的 BLAS 和 LAPACK 实现,不完全依赖外部库(尽管可以配置使用外部库)。 - 安全: 遵循 Rust 的安全原则,避免了许多传统高性能计算库中常见的 C/C++ 指针错误。
- 灵活的数据视图: 提供了灵活的数据视图 (
MatRef
,MatMut
),支持任意步长的切片和转置,零开销。 - 并行: 利用
rayon
实现高效的并行计算。 - 功能: 提供矩阵乘法、分解(LU, Cholesky, QR, SVD, EVD)、求解线性方程组等核心线性代数功能。
基本用法与特性:
-
创建矩阵:
“`rust
use faer::{Mat, prelude::*};// 创建拥有数据的矩阵
let mat = Mat::from_fn(3, 4, |i, j| (i + j) as f64);
println!(“mat:\n{}”, mat);// 从 Vec 创建
let data: Vec= (0..12).map(|i| i as f64).collect();
let mat_from_vec = Mat::from_column_major(4, 3, data); // Specify dimensions and layout
println!(“mat_from_vec:\n{}”, mat_from_vec);
“` -
视图和切片: 使用
mat.as_ref()
或mat.as_mut()
获取不可变或可变视图。视图操作高效。
“`rust
let mat = Mat::from_fn(4, 4, |i, j| (i * 4 + j) as f64);
let view = mat.as_ref().submatrix(1, 1, 2, 2); // submatrix(row_start, col_start, height, width)
println!(“view:\n{}”, view);
/ Output:
[[ 5, 6],
[ 9, 10]]
/let transposed_view = view.transpose();
println!(“transposed_view:\n{}”, transposed_view);
/ Output:
[[ 5, 9],
[ 6, 10]]
/
“` -
线性代数操作: 直接调用方法或使用函数。
“`rust
use faer::mat;let a = mat![
[1.0, 2.0],
[3.0, 4.0]
];
let b = mat![
[5.0, 6.0],
[7.0, 8.0]
];// Matrix multiplication (requires mutable output matrix)
let mut c = Mat::zeros(2, 2);
faer::mul::matmul(c.as_mut(), a.as_ref(), b.as_ref(), 1.0, 0.0); // c = 1.0 * a * b + 0.0 * c
println!(“a * b:\n{}”, c);// Solving linear system Ax = b
let a_solve = mat![
[2.0, 1.0],
[1.0, 1.0]
];
let b_solve = mat![
[4.0],
[3.0]
]; // b must be a matrix (or column vector)let lu_a = faer::linalg::lu::lu_decompose(a_solve.clone()).unwrap();
let x = lu_a.solve_ls(b_solve.as_ref());
println!(“Solution x for Ax=b:\n{}”, x); // Should be [1.0, 2.0]
``
faer` 的函数式 API 风格,很多操作需要一个可变引用作为输出参数,这有助于内存管理和避免不必要的分配。
注意
与 ndarray
和 nalgebra
的比较:
- 侧重点:
faer
纯粹专注于高性能线性代数计算,特别是大型矩阵。它不像ndarray
那样通用,也不像nalgebra
那样包含几何类型。 - 性能:
faer
在许多基准测试中显示出超越ndarray
(依赖外部 BLAS) 和nalgebra
的性能,尤其是在现代硬件上。 - API 风格:
faer
的 API 更偏向于高性能计算库的风格,例如,许多操作需要显式传递输出矩阵。 - 成熟度: 相对较新,但发展非常快,功能集正在迅速完善。
如果你的主要需求是进行大规模、高性能的线性代数计算,并且性能是关键考量,那么 faer
是一个非常有竞争力的选择。它可以与 ndarray
结合使用,比如在 ndarray
中处理数据,然后将视图传递给 faer
执行线性代数运算。
4. 其他值得关注的库
rustfft
: Rust 中的快速傅立叶变换 (FFT) 库。如果你的工作涉及信号处理或频谱分析,这个库是必不可少的。它提供了高效的一维和二维 FFT 实现。approx
: 一个用于近似比较浮点数的库。在数值计算中,直接使用==
比较浮点数通常是错误的。approx
提供了宏和 traits 来进行带有容差的比较,例如assert_relative_eq!
,abs_diff_eq!
,ulps_eq!
等。在测试数值代码时非常有用。ndarray-linalg
: 为ndarray
提供线性代数功能,底层通常调用外部 BLAS/LAPACK 库(如 OpenBLAS, Netlib LAPACK)。它是ndarray
的一个扩展,提供了诸如求逆、特征值、SVD 等功能。功能上与nalgebra
和faer
有重叠,但它是在ndarray
的数组类型之上工作的。- ** plotting 库:** 虽然不是直接的数值计算库,但在数据分析和可视化中,绘图是关键一环。Rust 中有
plotters
,eframe
/egui
集成 plotting 等库,可以用于绘制计算结果。或者,可以结合pyo3
和matplotlib
,在 Rust 中计算,将结果传回 Python 绘制。
Rust 与 Python (NumPy) 的互操作
很多时候,你可能不想完全抛弃现有的 Python 生态系统。幸运的是,Rust 和 Python 之间有很好的互操作性。
pyo3
: 这是一个非常成熟的 crate,允许你在 Rust 中编写 Python 模块,或在 Python 中嵌入 Rust 代码。rust-numpy
: 这个 crate 是pyo3
的一个扩展,它使得在 Rust 和 Python 之间传递 NumPyndarray
变得非常方便。你可以在 Rust 中接收一个PyReadonlyArray<f64, Ix2>
(对应 Python 的 NumPy 数组视图),使用ndarray
进行高效计算,然后返回一个新的PyArray<f64, Ix2>
(对应新的 NumPy 数组)。这是一种常见的模式,用于将计算密集型部分用 Rust 实现,而外围逻辑和可视化仍然在 Python 中完成。
这种互操作性使得渐进式地将性能瓶颈部分从 Python 迁移到 Rust 成为可能,无需一次性重写整个项目。
如何选择合适的库?
面对多个选择,如何决定使用哪个库呢?
- 通用 N 维数组操作、切片、广播: 优先考虑
ndarray
。它是 Rust 中最接近 NumPyndarray
的通用库,提供灵活的维度和视图管理。 - 线性代数作为核心任务 (向量、矩阵作为数学实体)、几何计算: 优先考虑
nalgebra
。特别是如果你处理固定大小的小型矩阵/向量,或者需要几何变换功能。 - 高性能、大规模线性代数计算: 优先考虑
faer
。如果你的应用对性能有极致要求,处理大型矩阵,并且愿意接受其特定的 API 风格。 - FFT 计算: 使用
rustfft
。 - 在
ndarray
之上进行线性代数: 如果你已经在大量使用ndarray
,并且只需要标准的线性代数功能,可以考虑ndarray-linalg
作为ndarray
的扩展。 - 浮点数比较: 使用
approx
。
在许多实际应用中,你可能需要组合使用这些库。例如,使用 ndarray
加载和预处理数据,使用 faer
或 nalgebra
执行核心的线性代数算法,然后使用 plotters
可视化结果。或者,通过 pyo3
和 rust-numpy
将 ndarray
集成到现有的 Python/NumPy 工作流程中。
总结与展望
Rust 的数值计算生态系统虽然没有 Python/NumPy 那样成熟和集中的地位,但它正在迅速发展,并提供了强大、安全、高性能的替代方案。
ndarray
是通用的 N 维数组操作基石,提供了类似 NumPy 的数组操作体验。nalgebra
是线性代数和几何领域的有力工具,尤其擅长固定大小矩阵和向量。faer
代表了 Rust 在高性能线性代数计算领域的最新进展,致力于提供世界级的性能。- 其他库如
rustfft
补充了特定领域的数值算法。 - 与 Python 生态的良好互操作性 (
pyo3
,rust-numpy
) 使得 Rust 可以作为 Python 科学计算的性能加速器。
选择 Rust 进行数值计算意味着更高的学习门槛和更分散的库生态,但也带来了极致的性能、编译时的安全保障、无 GC 的确定性以及更灵活的部署方式。对于需要构建高性能计算库、系统级数值应用或将计算密集型任务从 Python 迁移出来的场景,Rust 提供了非常吸引人的可能性。
随着 Rust 生态系统的不断成熟,我们有理由相信,它在科学计算领域的地位将越来越重要。现在正是探索 Rust 在数值计算方面潜力的好时机。通过本文介绍的库,您可以开始在 Rust 中构建高性能的数值应用,体验其带来的效率和可靠性。