Skip to content

Commit 313f9a3

Browse files
committed
FEAT: Add Zip::par_apply_collect, par_apply_assign_into
1 parent 6296b2c commit 313f9a3

File tree

2 files changed

+87
-8
lines changed

2 files changed

+87
-8
lines changed

src/parallel/impl_par_methods.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::{ArrayBase, DataMut, Dimension, NdProducer, Zip};
1+
use crate::{Array, ArrayBase, DataMut, Dimension, IntoNdProducer, NdProducer, Zip};
2+
use crate::AssignElem;
23

34
use crate::parallel::prelude::*;
45

@@ -43,7 +44,7 @@ where
4344
// Zip
4445

4546
macro_rules! zip_impl {
46-
($([$($p:ident)*],)+) => {
47+
($([$notlast:ident $($p:ident)*],)+) => {
4748
$(
4849
#[allow(non_snake_case)]
4950
impl<D, $($p),*> Zip<($($p,)*), D>
@@ -63,16 +64,52 @@ macro_rules! zip_impl {
6364
{
6465
self.into_par_iter().for_each(move |($($p,)*)| function($($p),*))
6566
}
67+
68+
expand_if!(@bool [$notlast]
69+
70+
/// Apply and collect the results into a new array, which has the same size as the
71+
/// inputs.
72+
///
73+
/// If all inputs are c- or f-order respectively, that is preserved in the output.
74+
///
75+
/// Restricted to functions that produce copyable results for technical reasons; other
76+
/// cases are not yet implemented.
77+
pub fn par_apply_collect<R>(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) -> Array<R, D>
78+
where R: Copy + Send
79+
{
80+
let mut output = self.uninitalized_for_current_layout::<R>();
81+
self.par_apply_assign_into(&mut output, f);
82+
unsafe {
83+
output.assume_init()
84+
}
85+
}
86+
87+
/// Apply and assign the results into the producer `into`, which should have the same
88+
/// size as the other inputs.
89+
///
90+
/// The producer should have assignable items as dictated by the `AssignElem` trait,
91+
/// for example `&mut R`.
92+
pub fn par_apply_assign_into<R, Q>(self, into: Q, f: impl Fn($($p::Item,)* ) -> R + Sync + Send)
93+
where Q: IntoNdProducer<Dim=D>,
94+
Q::Item: AssignElem<R> + Send,
95+
Q::Output: Send,
96+
{
97+
self.and(into)
98+
.par_apply(move |$($p, )* output_| {
99+
output_.assign_elem(f($($p ),*));
100+
});
101+
}
102+
);
66103
}
67104
)+
68105
}
69106
}
70107

71108
zip_impl! {
72-
[P1],
73-
[P1 P2],
74-
[P1 P2 P3],
75-
[P1 P2 P3 P4],
76-
[P1 P2 P3 P4 P5],
77-
[P1 P2 P3 P4 P5 P6],
109+
[true P1],
110+
[true P1 P2],
111+
[true P1 P2 P3],
112+
[true P1 P2 P3 P4],
113+
[true P1 P2 P3 P4 P5],
114+
[false P1 P2 P3 P4 P5 P6],
78115
}

tests/par_zip.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,45 @@ fn test_zip_index_4() {
7070
assert_eq!(*elt, j);
7171
}
7272
}
73+
74+
#[test]
75+
#[cfg(feature = "approx")]
76+
fn test_zip_collect() {
77+
use approx::assert_abs_diff_eq;
78+
79+
// test Zip::apply_collect and that it preserves c/f layout.
80+
81+
let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32);
82+
let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32));
83+
84+
{
85+
let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y);
86+
87+
assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6);
88+
assert_eq!(a.strides(), b.strides());
89+
}
90+
91+
{
92+
let b = b.t();
93+
let c = c.t();
94+
95+
let a = Zip::from(&b).and(&c).par_apply_collect(|x, y| x + y);
96+
97+
assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6);
98+
assert_eq!(a.strides(), b.strides());
99+
}
100+
}
101+
102+
#[test]
103+
#[cfg(feature = "approx")]
104+
fn test_zip_assign_into() {
105+
use approx::assert_abs_diff_eq;
106+
107+
let mut a = Array::<f32, _>::zeros((M, N));
108+
let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32);
109+
let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32));
110+
111+
Zip::from(&b).and(&c).par_apply_assign_into(&mut a, |x, y| x + y);
112+
113+
assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6);
114+
}

0 commit comments

Comments
 (0)