Skip to content

Commit ca2954d

Browse files
fix: address second round of feedback from bluss
1 parent 2e13bb2 commit ca2954d

1 file changed

Lines changed: 44 additions & 19 deletions

File tree

src/linalg/impl_linalg.rs

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,29 @@ impl_dots!(Ix1, Ix2);
222222
impl_dots!(Ix2, Ix1);
223223
impl_dots!(Ix2, Ix2);
224224

225+
fn nd_dot_non_contiguous<A, S1, S2, S3>(
226+
lhs: &ArrayBase<S1, IxDyn>, rhs: &ArrayBase<S2, Ix2>, out: &mut ArrayBase<S3, IxDyn>,
227+
) where
228+
A: LinalgScalar,
229+
S1: Data<Elem = A>,
230+
S2: Data<Elem = A>,
231+
S3: DataMut<Elem = A>,
232+
{
233+
let ndim = lhs.ndim();
234+
if ndim == 2 {
235+
// unwrap: converting a 2-D ArrayView/ArrayViewMut to Ix2 always succeeds.
236+
let lhs_2d = lhs.view().into_dimensionality::<Ix2>().unwrap();
237+
let mut out_2d = out.view_mut().into_dimensionality::<Ix2>().unwrap();
238+
general_mat_mul(A::one(), &*lhs_2d, &*rhs, A::zero(), &mut *out_2d);
239+
} else {
240+
Zip::from(lhs.axis_iter(Axis(0)))
241+
.and(out.axis_iter_mut(Axis(0)))
242+
.for_each(|lhs_slice, mut out_slice| {
243+
nd_dot_non_contiguous(&lhs_slice, rhs, &mut out_slice);
244+
});
245+
}
246+
}
247+
225248
macro_rules! impl_dot_nd_ix2 {
226249
($dim:ty) => {
227250
impl<A> Dot<ArrayRef<A, Ix2>> for ArrayRef<A, $dim>
@@ -247,8 +270,8 @@ macro_rules! impl_dot_nd_ix2 {
247270

248271
if self.is_standard_layout() {
249272
// C-contiguous: to_shape returns a *view* (no copy of LHS data).
250-
// Safety: rows * k == self.len() by construction.
251273
let rows = self.len() / k;
274+
// unwrap: rows * k == self.len() by construction, so reshape always succeeds.
252275
let lhs_2d = self.to_shape((rows, k)).unwrap();
253276
let result_2d = lhs_2d.dot(rhs);
254277

@@ -258,17 +281,19 @@ macro_rules! impl_dot_nd_ix2 {
258281
}
259282
out_dim[ndim - 1] = n;
260283

261-
// result_2d is a fresh C-contiguous owned array;
262-
// into_shape_with_order is free.
284+
// unwrap: result_2d is a fresh C-contiguous owned array of the correct size.
263285
result_2d.into_shape_with_order(out_dim).unwrap()
264286
} else {
265-
// Non-contiguous: iterate over the first axis so no whole-array
266-
// copy is needed. Each sub-array is (ndim-1)-D; the impl for that
267-
// dimension is already compiled (macro invocations are in order).
268-
let sub_results: Vec<_> =
269-
self.axis_iter(Axis(0)).map(|lane| lane.dot(rhs)).collect();
270-
let views: Vec<_> = sub_results.iter().map(|a| a.view()).collect();
271-
crate::stack(Axis(0), &views).unwrap()
287+
// Non-contiguous: iterate over the first axis and write results directly
288+
// into the pre-allocated output array to avoid whole-array copying.
289+
let mut out_dim = <$dim>::zeros(ndim);
290+
for i in 0..ndim - 1 {
291+
out_dim[i] = self.shape()[i];
292+
}
293+
out_dim[ndim - 1] = n;
294+
let mut out = Array::zeros(out_dim);
295+
nd_dot_non_contiguous(&self.view().into_dyn(), &rhs.view(), &mut out.view_mut().into_dyn());
296+
out
272297
}
273298
}
274299
}
@@ -305,24 +330,24 @@ where A: LinalgScalar
305330

306331
if self.is_standard_layout() {
307332
// C-contiguous: to_shape returns a *view* (no copy of LHS data).
308-
// Safety: rows * k == self.len() by construction.
309333
let rows = self.len() / k;
334+
// unwrap: rows * k == self.len() by construction, so reshape always succeeds.
310335
let lhs_2d = self.to_shape((rows, k)).unwrap();
311336
let result_2d = lhs_2d.dot(rhs);
312337

313338
let mut out_shape = self.shape().to_vec();
314339
*out_shape.last_mut().unwrap() = n;
315340

316-
// result_2d is a fresh C-contiguous owned array;
317-
// into_shape_with_order is free.
341+
// unwrap: result_2d is a fresh C-contiguous owned array of the correct size.
318342
result_2d.into_shape_with_order(IxDyn(&out_shape)).unwrap()
319343
} else {
320-
// Non-contiguous: iterate over the first axis so no whole-array
321-
// copy is needed. Each sub-array is (ndim-1)-D IxDyn; recursion
322-
// eventually reaches contiguous 2-D which terminates the recursion.
323-
let sub_results: Vec<_> = self.axis_iter(Axis(0)).map(|lane| lane.dot(rhs)).collect();
324-
let views: Vec<_> = sub_results.iter().map(|a| a.view()).collect();
325-
crate::stack(Axis(0), &views).unwrap()
344+
// Non-contiguous: iterate recursively over the first axis and write results
345+
// directly into the pre-allocated output array to avoid whole-array copying.
346+
let mut out_shape = self.shape().to_vec();
347+
*out_shape.last_mut().unwrap() = n;
348+
let mut out = Array::zeros(IxDyn(&out_shape));
349+
nd_dot_non_contiguous(&self.view(), &rhs.view(), &mut out.view_mut());
350+
out
326351
}
327352
}
328353
}

0 commit comments

Comments
 (0)