简体   繁体   English

如何使用人造丝更新包含 Rust 数组的个人结构

[英]How to use rayon to update a personal struct containing an Array in Rust

CONTEXT语境

General overview总体概述

(Here is the github page with the minimal example of my problem, and the page of my whole project) I'm very new to rust and I'm trying to simulate the behavior of a fluid in Rust. (这里是github 页面,其中包含我的问题的最小示例,以及我整个项目的页面)我对 Rust 很陌生,我正在尝试模拟 Rust 中流体的行为。 This is easy: computing large arrays with some functions for each timestep.这很简单:为每个时间步计算具有一些函数的大型数组。

I'd like to parallelize the computations done each timestep using rayon.我想使用人造丝并行化每个时间步完成的计算。 But the compiler doesn't want me to access a mutable struct containing an Array that I want to modify, even if I'm sure that each modification will be on different places in the array: which assure me that it's safe.但是编译器不希望我访问包含我要修改的数组的可变结构,即使我确定每次修改都会在数组中的不同位置:这向我保证它是安全的。 (I think?). (我认为?)。

So my question is: should I use unsafe rust in here?所以我的问题是:我应该在这里使用不安全的生锈吗? If so, how?如果是这样,如何? And: is it possible to make Rust understand that it's safe, or to do it properly without unsafe rust?并且:是否有可能让 Rust 明白它是安全的,或者在没有不安全 Rust 的情况下正确地做到这一点? I saw this post which kind of resembled my problem, but couldn't find a way to use the solution for my problem.我看到这篇文章有点像我的问题,但找不到使用该解决方案解决我的问题的方法。

I also tried to put unsafe {...} keywords everywhere, but the compilator still complains...我还尝试在各处放置unsafe {...}关键字,但编译器仍然抱怨...

You may only need to read "Structs" subsection to understand the problem, but I will also put a "Function" subsection, in case it can be important.您可能只需要阅读“结构”小节即可理解问题,但我还会放一个“功能”小节,以防它很重要。 If you think it might not be necessary, you can skip to "Main function" subsection.如果您认为可能没有必要,可以跳到“主要功能”小节。

Structs结构体

Here are my structs: I'd like to keep them that way, as they would give me (I think) more flexibility with setters/getters, but I'm open to change the way it's implemented right now.这是我的结构:我想保持这种方式,因为它们会给我(我认为)更多的 setter/getter 灵活性,但我愿意改变它现在的实现方式。

#[derive(Debug, PartialEq)]
struct vec2D {pub x: f64, pub y: f64}

#[derive(Debug, PartialEq)]
 struct ScalarField2D {
     s: Array2<f64>,
}    

#[derive(Debug, PartialEq)]
 struct VectorField2D {
     x: ScalarField2D,
     y: ScalarField2D
}

impl ScalarField2D {

     // also a constructor new() but not shown for simplicity

     fn get_pos(&self, x: usize, y: usize) -> f64
    {return self.s[[y,x]];}

    fn set_pos(&mut self, x: usize, y: usize, f: f64)
    {self.s[[y,x]] = f;}
}

impl VectorField2D {

     // also a constructor new() but not shown for simplicity

     fn get_pos(&self, x: usize, y: usize) -> vec2D
    {let vec_at_pos = vec2D {
        x: self.x.get_pos(x, y),
        y: self.y.get_pos(x, y)};
     return vec_at_pos;}

    fn set_pos(&mut self, x: usize, y: usize, vec: &vec2D)
    {self.x.set_pos(x, y, vec.x);
     self.y.set_pos(x, y, vec.y);}

}

Function功能

Here is my function: which takes a ScalarField2D struct, and computes a vector called the "gradient" at a particular position of the ScalarField2D array, and then returning this vector as a "vec2D" struct.这是我的函数:它采用 ScalarField2D 结构,并在 ScalarField2D 数组的特定位置计算称为“梯度”的向量,然后将该向量作为“vec2D”结构返回。

// computes the gradient of a scalar field at a given position
fn grad_scalar(a: &ScalarField2D,
               x: i32, y: i32,
               x_max: i32, y_max: i32) -> vec2D
{
    let ip = ((x+1) % x_max) as usize;
    // i-1 with Periodic Boundaries
    let im = ((x - 1 + x_max) % x_max) as usize;
    // j+1 with Periodic Boundaries
    let jp = ((y+1) % y_max) as usize;
    // j-1 with Periodic Boundaries
    let jm = ((y - 1 + y_max) % y_max) as usize;
    let (i, j) = (x as usize, y as usize);

    let grad = vec2D {
        x: (a.get_pos(ip, j) - a.get_pos(im, j))/(2.),
        y: (a.get_pos(i, jp) - a.get_pos(i, jm))/(2.)};
        
    return grad;
}

Main function主功能

Here is my problem: I try to iterate over all the possible x and y using (0..x_max).into_par_iter() (or y_max for y), compute the gradient associated with each position, and then set the value to the ScalarField2D struct using the set_pos method... Here is the main function, and the import commands, and I will show you the error message I get in the next subsection这是我的问题:我尝试使用 (0..x_max).into_par_iter()(或 y_max for y)遍历所有可能的 x 和 y,计算与每个位置关联的梯度,然后将值设置为 ScalarField2D使用 set_pos 方法的结构...这是主要功能和导入命令,我将在下一小节中向您展示我收到的错误消息

use ndarray::prelude::*;
use rayon::prelude::*;

fn main() {

    let (x_max, y_max) = (2usize, 50usize);
    let (x_maxi32, y_maxi32) = (x_max as i32, y_max as i32);

    let mut GD_grad_rho = VectorField2D::new(x_max, y_max);
    let mut GD_rho = ScalarField2D::new(x_max, y_max);    

    let x_iterator = (0..x_max).into_par_iter();
    x_iterator.map(|xi| {
        let y_iterator = (0..y_max).into_par_iter();
        y_iterator.map(|yi| {

            // unsafe here?
            GD_grad_rho
                .set_pos(xi, yi,
                         &grad_scalar(&GD_rho,
                                      xi as i32, yi as i32,
                                      x_maxi32, y_maxi32));
            
        });});
}

Error message错误信息

Here is the compilation error I get这是我得到的编译错误

error[E0596]: cannot borrow `GD_grad_rho` as mutable, as it is a captured variable in a `Fn` closure
   --> src/main.rs:104:13
    |
104 | /             GD_grad_rho
105 | |                 .set_pos(xi, yi,
106 | |                          &grad_scalar(&GD_rho,
107 | |                                       xi as i32, yi as i32,
108 | |                                       x_maxi32, y_maxi32));
    | |__________________________________________________________^ cannot borrow as mutable

error[E0596]: cannot borrow `GD_grad_rho` as mutable, as it is a captured variable in a `Fn` closure
   --> src/main.rs:101:24
    |
101 |         y_iterator.map(|yi| {
    |                        ^^^^ cannot borrow as mutable
...
104 |             GD_grad_rho
    |             ----------- mutable borrow occurs due to use of `GD_grad_rho` in closure

For more information about this error, try `rustc --explain E0596`.
error: could not compile `minimal_example_para` due to 2 previous errors

If you want the complete thing, I created a github repo with everything in it.如果您想要完整的东西,我创建了一个包含所有内容的github 存储库

Tests after susitsm answer susitsm 回答后的测试

So I did something like this:所以我做了这样的事情:

use ndarray::prelude::*;
use rayon::prelude::*;

fn grad_scalar(a: &Array2<f64>,
          i: usize, j: usize) -> (f64, f64)
{
    let array_shape = a.shape();
    let imax = array_shape[0];
    let jmax = array_shape[1];

    // i-1 with Periodic Boundaries
    let ip = ((i + 1) % imax);
    // i-1 with Periodic Boundaries
    let im = ((i + imax) - 1) % imax;
    // j+1 with Periodic Boundaries
    let jp = ((j + 1) % jmax);
    // j-1 with Periodic Boundaries
    let jm = ((j + jmax) - 1) % jmax;

    let grad_i = (a[[ip, j]] - a[[im, j]])/2.;
    let grad_j = (a[[i, jp]] - a[[i, jm]])/2.;

    return (grad_i, grad_j);
}

fn main() {

    let a = Array::<f64, Ix2>::ones((dim, dim));

    let index_list: Vec<(_, _)> = (0..a.len())
        .into_par_iter()
        .map(|i| (i / a.dim().0, i % a.dim().1))
        .collect();

    let (r1, r2): (Vec<_>, Vec<_>) = (0..a.len())
        .into_par_iter()
        .map(|i| (index_list[i].0, index_list[i].1))
        .map(|(i, j)| grad_scalar(&a, i, j))
        .collect();

    let grad_row = Array2::from_shape_vec(a.dim(), r1).unwrap();
    let grad_col = Array2::from_shape_vec(a.dim(), r2).unwrap();

}

Which gives me the result I want, even though I wanted to access the values through my Structs.这给了我想要的结果,即使我想通过我的结构访问这些值。 Which is not exactly what I want but we're getting closer But I wonder about the efficiency for more arrays, I'll post a separate question for that这不是我想要的,但我们越来越近了但我想知道更多阵列的效率,我会为此发布一个单独的问题

You can do something like this:你可以这样做:

use ndarray::Array2;
use rayon::prelude::*;

fn main() {
    let a: Vec<u64> = (0..20000).collect();
    let a = Array2::from_shape_vec((100, 200), a).unwrap();

    let stuff = |arr, i, j| (i + j, i * j);
    let (x, y): (Vec<_>, Vec<_>) = (0..a.len())
        .into_par_iter()
        .map(|i| (i / a.dim().0, i % a.dim().1))
        .map(|(i, j)| stuff(&a, i, j))
        .collect();
    let grad_x = Array2::from_shape_vec(a.dim(), x);
    let grad_y = Array2::from_shape_vec(a.dim(), y);
}

You could also implement FromParallelIterator<vec2D> for VectorField2D to make it easier to use`您还可以为 VectorField2D 实现FromParallelIterator<vec2D> VectorField2D使其更易于使用`

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM