回答編集履歴

5

Zip::par_map_collectを使った、より効率的なコードを追記しました

2021/04/09 23:37

投稿

tatsuya6502
tatsuya6502

スコア2035

test CHANGED
@@ -219,3 +219,127 @@
219
219
  }
220
220
 
221
221
  ```
222
+
223
+
224
+
225
+ **追記(2021年4月10日)**
226
+
227
+
228
+
229
+ 上で紹介した方法は、`collect()`のたびに新しい`Vec<_>`を作るため、ご質問の元のコードに比べて、その分が余分な処理になっています。
230
+
231
+
232
+
233
+ ndarrayのドキュメントをもう少し読んでみたところ、もっと効率の良いやり方があることに気づきました。以下の2点を組み合わせます。
234
+
235
+
236
+
237
+ * `par_windows()`ではなく、`c`のndarrayスライスを2つ作って`Zip`で対にする
238
+
239
+ * `Zip`の`par_map_collect()`を使用する。これは、Rayonの並列イテレーター作成 → `map` → `collect`をまとめてやってくれる。
240
+
241
+
242
+
243
+ この方法ならコードが簡単になるうえ、`Vec<_>`を作る必要がないので効率よく処理できるはずです。
244
+
245
+
246
+
247
+ まず、`par_map_collect()`を使うためにndarrayを最新の0.15.xにします。また`rayon`フィーチャーを指定します。
248
+
249
+
250
+
251
+ ```toml
252
+
253
+ # Cargo.tomlを修正する
254
+
255
+
256
+
257
+ [dependencies]
258
+
259
+ ndarray = { version = "0.15.1", features = ["rayon"] }
260
+
261
+ ```
262
+
263
+
264
+
265
+ `use`文と`cal_v()`関数を修正します。
266
+
267
+
268
+
269
+ ```rust
270
+
271
+ use ndarray::{arr3, s, Array3, Zip};
272
+
273
+
274
+
275
+ fn cal_v(c: &Array3<f32>, dt: f32) -> Array3<f32> {
276
+
277
+ let c_len0 = c.shape()[0];
278
+
279
+
280
+
281
+ // 1つ目のスライスは1次元目(time)の最初の要素から、最後の1つ前の要素まで
282
+
283
+ let c_before = c.slice(s![..(c_len0 - 1), .., ..]);
284
+
285
+ // 2つ目のスライスは1次元目(time)の2番目の要素から、最後の要素まで
286
+
287
+ let c_now = c.slice(s![1.., .., ..]);
288
+
289
+
290
+
291
+ // 2つのスライスをzipして、par_map_collectで並列処理する
292
+
293
+ Zip::from(c_before)
294
+
295
+ .and(c_now)
296
+
297
+ .par_map_collect(|before, now| (before - now) / dt)
298
+
299
+ }
300
+
301
+ ```
302
+
303
+
304
+
305
+ ただし、この方法を使うと、ご質問のコードとは異なり、計算結果`v`の1次元目の最初の要素(値が全て`0`)が作られなくなります。つまり、以下の`main()`関数の`expected`のコメントアウトした行のデータがなくなります。
306
+
307
+
308
+
309
+ ```rust
310
+
311
+ fn main() {
312
+
313
+ let c = arr3(&[
314
+
315
+ [[1.0, -2.0, 3.0], [-1.0, 2.0, -3.0]],
316
+
317
+ [[6.0, -5.0, 4.0], [-9.0, 8.0, -7.0]],
318
+
319
+ [[-12.0, 11.0, -10.0], [15.0, -14.0, 13.0]],
320
+
321
+ ]);
322
+
323
+ let v = cal_v(&c, 0.25);
324
+
325
+ println!("{}", v);
326
+
327
+
328
+
329
+ let expected = arr3(&[
330
+
331
+ // [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], // この要素は作られなくなる
332
+
333
+ [[-20.0, 12.0, -4.0], [32.0, -24.0, 16.0]],
334
+
335
+ [[72.0, -64.0, 56.0], [-96.0, 88.0, -80.0]],
336
+
337
+ ]);
338
+
339
+
340
+
341
+ assert_eq!(v, expected);
342
+
343
+ }
344
+
345
+ ```

4

誤字を修正しました

2021/04/09 23:37

投稿

tatsuya6502
tatsuya6502

スコア2035

test CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
 
8
8
 
9
- そこで、今回は`c`の`Array3<f32>`をまず`Vec<ArrayView2D<&f32>>`に変換し、そこからRayonの`par_windows()`を使って2要素ずつの並列イテレーターを作ることにします。
9
+ そこで、今回は`c`の`Array3<f32>`をまず`Vec<ArrayView2<&f32>>`に変換し、そこからRayonの`par_windows()`を使って2要素ずつの並列イテレーターを作ることにします。
10
10
 
11
11
 
12
12
 

3

不要な型注釈(v: Array3<_>)を削除しました

2021/04/09 16:10

投稿

tatsuya6502
tatsuya6502

スコア2035

test CHANGED
@@ -106,7 +106,7 @@
106
106
 
107
107
  // Vec<Array2<f32>>をArray3<f32>に変換する。もっと簡単に書く方法があるかも
108
108
 
109
- let mut v: Array3<_> = Array::zeros(c.raw_dim());
109
+ let mut v = Array::zeros(c.raw_dim());
110
110
 
111
111
  for (time, now_v) in v_array2_vec.into_iter().enumerate() {
112
112
 
@@ -170,7 +170,7 @@
170
170
 
171
171
  // Vec<Array2<f32>>をArray3<f32>に変換する。もっと簡単に書く方法があるかも
172
172
 
173
- let mut v: Array3<_> = Array::zeros(c.raw_dim());
173
+ let mut v = Array::zeros(c.raw_dim());
174
174
 
175
175
  for (time, now_v) in v_array2_vec.into_iter().enumerate() {
176
176
 

2

誤字を修正しました

2021/04/09 15:10

投稿

tatsuya6502
tatsuya6502

スコア2035

test CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ndarrayはCargo.tomlで`rayon`フィーチャーを指定すると、`Array3`などに`par_iter()`メソッドが追加され、直接Rayonの並列イテレーターを作れるようになります。ところが、ご質問のコードではこのフィーチャーは使えません。なぜなら`for`の1回のループ内でアクセスする要素(ndarrayのスライス)が1つではなく、`time - 1`と`time`で指定した2つだからです。Rayonにはこういう複数の要素ずつイテレートするために、`par_windows()`が用意されていますが、ndarrayの`rayon`フィーチャーは`par_windows()`をサポートしていません。
6
6
 
7
-
7
+
8
8
 
9
9
  そこで、今回は`c`の`Array3<f32>`をまず`Vec<ArrayView2D<&f32>>`に変換し、そこからRayonの`par_windows()`を使って2要素ずつの並列イテレーターを作ることにします。
10
10
 

1

誤字を修正しました。

2021/04/09 15:07

投稿

tatsuya6502
tatsuya6502

スコア2035

test CHANGED
@@ -56,7 +56,7 @@
56
56
 
57
57
 
58
58
 
59
- 次に`par_iter()`による並列計算です。
59
+ 次に`par_windows()`による並列計算です。
60
60
 
61
61
 
62
62
 
@@ -92,7 +92,7 @@
92
92
 
93
93
 
94
94
 
95
- 最後に`Vec<Array2<f32>>`から`Array3<f32>`に変換します。が、私はndarrayを使うのが初めてなことがあり、簡単に書く方法がわかりませんでした。しかたがないので`for`と`assign`で変換しています。
95
+ 最後に`Vec<Array2<f32>>`から`Array3<f32>`に変換します。が、私はndarrayを使うのが初めてなことがあり、簡単に書く方法がわかりませんでした。しかたがないのでご質問のコードを真似て`for`と`assign`で変換しています。
96
96
 
97
97
 
98
98