pub fn zip_reduce<A, B, OpA, OpB, M, R, U>(
a: &StridedView<'_, A, OpA>,
b: &StridedView<'_, B, OpB>,
map_fn: M,
reduce_fn: R,
init: U,
) -> Result<U>where
A: Copy + MaybeSendSync,
B: Copy + MaybeSendSync,
OpA: ElementOp<A>,
OpB: ElementOp<B>,
M: Fn(A, B) -> U + MaybeSync,
R: Fn(U, U) -> U + MaybeSync,
U: Clone + MaybeSendSync,Expand description
Two-input mapped reduction: reduce(init, op, map.(a[i], b[i])).
This is the two-operand sibling of reduce. It exists so that the
accumulator type U is chosen by the caller instead of being tied to the
operand types by Mul, which is what makes a widening accumulation
possible — for example an f32 dot accumulated in f64:
use strided_basic::{zip_reduce, StridedArray};
let a = StridedArray::<f32>::from_fn_col_major(&[2, 3], |i| (1 + i[0] + 2 * i[1]) as f32);
let b = StridedArray::<f32>::from_fn_col_major(&[2, 3], |i| (1 + i[0]) as f32 * 0.5);
let dot: f64 = zip_reduce(
&a.view(),
&b.view(),
|x: f32, y: f32| x as f64 * y as f64,
|x, y| x + y,
0.0,
)
.unwrap();
assert_eq!(dot, 16.5);Both operands must have the same shape. Each operand’s ElementOp is
applied before map_fn, so conjugation and transposition flags are honoured
without materializing a conjugated copy. Permuted, gapped, negative and
stride-0 (broadcast) operands are supported: the traversal is the fused
plan of reduce, extended to two stride lists.
§Evaluation order
The result is independent of the ambient pool width when the call runs
under the sequential policy — ExecContext::serial
or with_execution_policy with
ExecutionPolicy::Sequential: only
serial branches run, and their fold order is fixed by the operand layouts.
Under a parallel policy the fold is split over the effective thread count,
so the result is repeatable for a fixed shape, layout and thread count but
can differ between thread counts.
In neither case is this a strict left to right fold: a contiguous operand
pair is folded with the same sixteen independent lanes as reduce, the
general plan is traversed in a cache-friendly loop order, and parallel
execution folds per-worker chunks independently and combines them in a
tree. Floating point results can therefore differ from a sequential left to
right sum in the last bits, and no bitwise equality across machines or
target features is guaranteed.
Because of that, reduce_fn must be associative and commutative, and
init must be an identity of reduce_fn whenever the reduction can run on
several threads.
§Allocation
At rank ≤ INLINE_RANK the view metadata and
the two-operand traversal plan are stored inline, so a call performs no heap
allocation. Above that bound the planner may spill to the heap.
§Errors
Returns StridedError::RankMismatch / StridedError::ShapeMismatch when
the operand shapes differ, and StridedError::OffsetOverflow for
unrepresentable element counts.