我只是想为Rust-ndarray做一些基本的数学运算(例如,sin、exp、log、sqrt…(。然而,我并没有从ndarray的文档中找到任何有用的例子。
例如:
extern crate ndarray;
use ndarray as nd;
fn main() {
let matrix = nd::array![[1., 2., 3.], [9., 8., 7.]];
let result = some_math(matrix);
println!("{}", result)
}
fn some_math(...) {
//Here I would like to do elementwise exp() and sqrt
sqrt(exp(...))
// Using f64::exp would fail.
}
如何有效地实现some_math
?我当然可以通过循环矩阵的元素来进行元素操作,但这听起来不太好,我不喜欢这样做
在python的numpy
中,这就是np.sqrt(np.exp(matrix))
。我的意思是Rust确实是一种很棒的语言,然而,即使是简单的代数也很不方便(缺乏适当的生态系统(。
更新:ndarray正在进行拉取请求。如果这被接受,那么你可以简单地进行matrix.exp().sqrt()
等
ndarray文档中有一个非常隐藏的页面,告诉您如何进行此类数学运算。
一些相关问题:12
如何高效地实现这样的
some_math
?
您可以使用mapv_into()
:
use ndarray as nd;
use ndarray::Array2;
fn some_math(matrix: Array2<f64>) -> Array2<f64> {
// np.sqrt(np.exp(matrix)) would literally translate to equivalent to
// matrix.mapv_into(f64::exp).mapv_into(f64::sqrt)
// but this version iterates over the matrix just once
matrix.mapv_into(|v| v.exp().sqrt())
}
fn main() {
let matrix = nd::array![[1., 2., 3.], [9., 8., 7.]];
let result = some_math(matrix);
println!("{:?}", result)
}
游乐场
这应该会使您的性能与numpy
相当,但您应该进行测量以确定。
要使用多个核心(这对大型阵列来说是有意义的(,您需要启用机箱的rayon
功能并使用par_mapv_inplace()
:
fn some_math(mut matrix: Array2<f64>) -> Array2<f64> {
matrix.par_mapv_inplace(|v| v.exp().sqrt());
matrix
}
(由于游乐场的ndarray
不包括rayon
功能,因此无法在游乐场上编译。(
请注意,在上面的示例中,如果感觉更自然,可以用f64::sqrt(f64::exp(v))
替换v.exp().sqrt()
。
EDIT:我对timnings很好奇,所以我决定做一个琐碎的(不科学的(基准测试——创建一个随机的10_000x10_0000数组,并将np.sqrt(np.sqrt(array))
与Rust等效数组进行比较。
用于基准测试的Python代码:
import numpy as np
import time
matrix = np.random.rand(10000, 10000)
t0 = time.time()
np.sqrt(np.exp(matrix))
t1 = time.time()
print(t1 - t0)
锈蚀代码:
use std::time::Instant;
use ndarray::Array2;
use ndarray_rand::{RandomExt, rand_distr::Uniform};
fn main() {
let matrix: Array2<f64> = Array2::random((10000, 10000), Uniform::new(0., 1.));
let t0 = Instant::now();
let _result = matrix.mapv_into(|v| v.exp().sqrt());
let elapsed = t0.elapsed();
println!("{}", elapsed.as_secs_f64());
}
在我对古老桌面系统的实验中,Python需要3.7s来计算,而Rust需要2.5s.5 s,比同等的Python快7.4倍。
单线程Rust版本更快是有道理的,因为它只在整个数组上迭代一次,而Python迭代两次。如果我们删除sqrt()
操作,Python的时钟为2.8秒,而Rust的时钟仍略快,为2.4秒(仍为0.5秒并行(。我不确定是否可以在不使用numba之类的东西的情况下优化Python版本。事实上,能够在不因手动进行低级别计算而遭受性能损失的情况下调整代码,这是像Rust这样的编译语言的好处。
我不知道如何在Python中复制多线程版本,但了解numba的人可以做这件事并进行比较。