@@ -222,6 +222,29 @@ impl_dots!(Ix1, Ix2);
222222impl_dots ! ( Ix2 , Ix1 ) ;
223223impl_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+
225248macro_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