1use crate::error::{LinalgError, LinalgResult};
8use scirs2_core::ndarray::{Array2, ArrayView2, Axis, ScalarOperand};
9use scirs2_core::numeric::{Float, NumAssign};
10use scirs2_core::parallel_ops::*;
11use std::cmp;
12use std::iter::Sum;
13
14#[derive(Debug, Clone, Copy)]
16pub enum OptAlgorithm {
17 Standard,
19 Blocked,
21 Parallel,
23 Adaptive,
25}
26
27#[derive(Debug, Clone)]
29pub struct OptConfig {
30 pub blocksize: usize,
32 pub parallel_threshold: usize,
34 pub num_threads: Option<usize>,
36 pub algorithm: OptAlgorithm,
38}
39
40impl Default for OptConfig {
41 fn default() -> Self {
42 OptConfig {
43 blocksize: 64,
44 parallel_threshold: 1000,
45 num_threads: None,
46 algorithm: OptAlgorithm::Blocked,
47 }
48 }
49}
50
51impl OptConfig {
52 pub fn with_blocksize(mut self, size: usize) -> Self {
54 self.blocksize = size;
55 self
56 }
57
58 pub fn with_parallel_threshold(mut self, threshold: usize) -> Self {
59 self.parallel_threshold = threshold;
60 self
61 }
62
63 pub fn with_num_threads(mut self, threads: usize) -> Self {
64 self.num_threads = Some(threads);
65 self
66 }
67
68 pub fn with_algorithm(mut self, algorithm: OptAlgorithm) -> Self {
69 self.algorithm = algorithm;
70 self
71 }
72}
73
74#[allow(dead_code)]
79pub fn blocked_matmul<F>(
80 a: &ArrayView2<F>,
81 b: &ArrayView2<F>,
82 config: &OptConfig,
83) -> LinalgResult<Array2<F>>
84where
85 F: Float + NumAssign + Sum + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
86{
87 let (m, k) = (a.nrows(), a.ncols());
88 let (k2, n) = (b.nrows(), b.ncols());
89
90 if k != k2 {
91 return Err(LinalgError::DimensionError(format!(
92 "Matrix dimensions don't match: ({m}, {k}) x ({k2}, {n})"
93 )));
94 }
95
96 let mut c = Array2::zeros((m, n));
97 let blocksize = config.blocksize;
98
99 match config.algorithm {
100 OptAlgorithm::Standard => Ok(a.dot(b)),
101 OptAlgorithm::Blocked => {
102 serial_blocked_matmul(a, b, &mut c, blocksize)?;
103 Ok(c)
104 }
105 OptAlgorithm::Parallel => {
106 parallel_blocked_matmul(a, b, &mut c, blocksize)?;
107 Ok(c)
108 }
109 OptAlgorithm::Adaptive => {
110 if m * n > config.parallel_threshold {
112 parallel_blocked_matmul(a, b, &mut c, blocksize)?;
113 } else if m * n > 10000 {
114 serial_blocked_matmul(a, b, &mut c, blocksize)?;
115 } else {
116 return Ok(a.dot(b));
117 }
118 Ok(c)
119 }
120 }
121}
122
123#[allow(dead_code)]
125fn serial_blocked_matmul<F>(
126 a: &ArrayView2<F>,
127 b: &ArrayView2<F>,
128 c: &mut Array2<F>,
129 blocksize: usize,
130) -> LinalgResult<()>
131where
132 F: Float + NumAssign + Sum + Send + Sync + ScalarOperand + 'static,
133{
134 let (m, k) = (a.nrows(), a.ncols());
135 let n = b.ncols();
136
137 for ii in (0..m).step_by(blocksize) {
139 for jj in (0..n).step_by(blocksize) {
140 for kk in (0..k).step_by(blocksize) {
141 let i_end = cmp::min(ii + blocksize, m);
143 let j_end = cmp::min(jj + blocksize, n);
144 let k_end = cmp::min(kk + blocksize, k);
145
146 for i in ii..i_end {
147 for j in jj..j_end {
148 let mut sum = c[[i, j]];
149 for ki in kk..k_end {
150 sum += a[[i, ki]] * b[[ki, j]];
151 }
152 c[[i, j]] = sum;
153 }
154 }
155 }
156 }
157 }
158
159 Ok(())
160}
161
162#[allow(dead_code)]
164fn parallel_blocked_matmul<F>(
165 a: &ArrayView2<F>,
166 b: &ArrayView2<F>,
167 c: &mut Array2<F>,
168 blocksize: usize,
169) -> LinalgResult<()>
170where
171 F: Float + NumAssign + Sum + Send + Sync,
172{
173 let (m, k) = (a.nrows(), a.ncols());
174 let n = b.ncols();
175
176 let block_indices: Vec<(usize, usize)> = (0..m)
178 .step_by(blocksize)
179 .flat_map(|i| (0..n).step_by(blocksize).map(move |j| (i, j)))
180 .collect();
181
182 let results: Vec<_> = parallel_map(&block_indices, |&(ii, jj)| {
184 let i_end = cmp::min(ii + blocksize, m);
185 let j_end = cmp::min(jj + blocksize, n);
186
187 let mut local_c = Array2::zeros((i_end - ii, j_end - jj));
189
190 for kk in (0..k).step_by(blocksize) {
192 let k_end = cmp::min(kk + blocksize, k);
193
194 for (i_local, i) in (0..(i_end - ii)).zip(ii..i_end) {
195 for (j_local, j) in (0..(j_end - jj)).zip(jj..j_end) {
196 let mut sum = local_c[[i_local, j_local]];
197 for ki in kk..k_end {
198 sum += a[[i, ki]] * b[[ki, j]];
199 }
200 local_c[[i_local, j_local]] = sum;
201 }
202 }
203 }
204
205 ((ii, jj), local_c)
207 });
208
209 for ((ii, jj), local_c) in results {
211 let i_end = cmp::min(ii + blocksize, m);
212 let j_end = cmp::min(jj + blocksize, n);
213
214 for (i_local, i) in (0..(i_end - ii)).zip(ii..i_end) {
215 for (j_local, j) in (0..(j_end - jj)).zip(jj..j_end) {
216 c[[i, j]] = local_c[[i_local, j_local]];
217 }
218 }
219 }
220
221 Ok(())
222}
223
224#[allow(dead_code)]
226pub fn blocked_matmul_with_workers<F>(
227 a: &ArrayView2<F>,
228 b: &ArrayView2<F>,
229 workers: Option<usize>,
230) -> LinalgResult<Array2<F>>
231where
232 F: Float + NumAssign + Sum + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
233{
234 use crate::parallel;
235
236 parallel::configure_workers(workers);
238
239 let config = OptConfig {
240 num_threads: workers,
241 ..OptConfig::default()
242 };
243
244 blocked_matmul(a, b, &config)
245}
246
247#[allow(dead_code)]
249pub fn optimized_transpose<F>(a: &ArrayView2<F>) -> LinalgResult<Array2<F>>
250where
251 F: Float + Send + Sync,
252{
253 optimized_transpose_with_workers(a, None)
254}
255
256#[allow(dead_code)]
258pub fn optimized_transpose_with_workers<F>(
259 a: &ArrayView2<F>,
260 workers: Option<usize>,
261) -> LinalgResult<Array2<F>>
262where
263 F: Float + Send + Sync,
264{
265 use crate::parallel;
266
267 parallel::configure_workers(workers);
269 let (m, n) = (a.nrows(), a.ncols());
270 let mut result = Array2::zeros((n, m));
271
272 let blocksize = 32;
274
275 for i in (0..m).step_by(blocksize) {
276 for j in (0..n).step_by(blocksize) {
277 let i_end = cmp::min(i + blocksize, m);
278 let j_end = cmp::min(j + blocksize, n);
279
280 for ii in i..i_end {
282 for jj in j..j_end {
283 result[[jj, ii]] = a[[ii, jj]];
284 }
285 }
286 }
287 }
288
289 Ok(result)
290}
291
292#[allow(dead_code)]
294pub fn parallel_matvec<F>(
295 a: &ArrayView2<F>,
296 x: &ArrayView2<F>,
297 config: &OptConfig,
298) -> LinalgResult<Array2<F>>
299where
300 F: Float + NumAssign + Sum + Send + Sync,
301{
302 if a.ncols() != x.nrows() {
303 return Err(LinalgError::DimensionError(format!(
304 "Matrix and vector dimensions don't match: ({}, {}) x ({}, {})",
305 a.nrows(),
306 a.ncols(),
307 x.nrows(),
308 x.ncols()
309 )));
310 }
311
312 let m = a.nrows();
313 let n = x.ncols();
314 let mut result = Array2::zeros((m, n));
315
316 if m > config.parallel_threshold {
317 let rows: Vec<_> = result.axis_iter_mut(Axis(0)).enumerate().collect();
319
320 rows.into_par_iter().for_each(|(i, mut row)| {
322 for j in 0..n {
323 let mut sum = F::zero();
324 for k in 0..a.ncols() {
325 sum += a[[i, k]] * x[[k, j]];
326 }
327 row[j] = sum;
328 }
329 });
330 } else {
331 for i in 0..m {
333 for j in 0..n {
334 let mut sum = F::zero();
335 for k in 0..a.ncols() {
336 sum += a[[i, k]] * x[[k, j]];
337 }
338 result[[i, j]] = sum;
339 }
340 }
341 }
342
343 Ok(result)
344}
345
346#[allow(dead_code)]
348pub fn parallel_matvec_with_workers<F>(
349 a: &ArrayView2<F>,
350 x: &ArrayView2<F>,
351 workers: Option<usize>,
352) -> LinalgResult<Array2<F>>
353where
354 F: Float + NumAssign + Sum + Send + Sync,
355{
356 use crate::parallel;
357
358 parallel::configure_workers(workers);
360
361 let config = OptConfig {
362 num_threads: workers,
363 ..OptConfig::default()
364 };
365
366 parallel_matvec(a, x, &config)
367}
368
369pub mod inplace {
371 use super::*;
372
373 pub fn add_assign<F>(a: &mut Array2<F>, b: &ArrayView2<F>) -> LinalgResult<()>
375 where
376 F: Float + NumAssign,
377 {
378 if a.shape() != b.shape() {
379 return Err(LinalgError::DimensionError(format!(
380 "Matrix dimensions don't match: {:?} != {:?}",
381 a.shape(),
382 b.shape()
383 )));
384 }
385
386 for (a_elem, b_elem) in a.iter_mut().zip(b.iter()) {
387 *a_elem += *b_elem;
388 }
389
390 Ok(())
391 }
392
393 pub fn scalar_mul_assign<F>(a: &mut Array2<F>, scalar: F) -> LinalgResult<()>
395 where
396 F: Float + NumAssign + Send + Sync,
397 {
398 for elem in a.iter_mut() {
399 *elem *= scalar;
400 }
401
402 Ok(())
403 }
404
405 pub fn transpose_square<F>(a: &mut Array2<F>) -> LinalgResult<()>
407 where
408 F: Float,
409 {
410 let n = a.nrows();
411 if n != a.ncols() {
412 return Err(LinalgError::DimensionError(
413 "In-place transpose requires square matrix".to_string(),
414 ));
415 }
416
417 for i in 0..n {
418 for j in (i + 1)..n {
419 let temp = a[[i, j]];
420 a[[i, j]] = a[[j, i]];
421 a[[j, i]] = temp;
422 }
423 }
424
425 Ok(())
426 }
427}
428
429#[allow(dead_code)]
431pub fn adaptive_matmul<F>(a: &ArrayView2<F>, b: &ArrayView2<F>) -> LinalgResult<Array2<F>>
432where
433 F: Float + NumAssign + Sum + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
434{
435 adaptive_matmul_with_workers(a, b, None)
436}
437
438#[allow(dead_code)]
440pub fn adaptive_matmul_with_workers<F>(
441 a: &ArrayView2<F>,
442 b: &ArrayView2<F>,
443 workers: Option<usize>,
444) -> LinalgResult<Array2<F>>
445where
446 F: Float + NumAssign + Sum + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
447{
448 use crate::parallel;
449
450 parallel::configure_workers(workers);
452
453 let size = a.nrows() * a.ncols() + b.nrows() * b.ncols();
454
455 if size < 10000 {
457 Ok(a.dot(b))
459 } else if size < 1000000 {
460 let config = OptConfig {
462 num_threads: workers,
463 ..OptConfig::default()
464 };
465 blocked_matmul(a, b, &config)
466 } else {
467 let config = OptConfig {
469 parallel_threshold: 50000,
470 num_threads: workers,
471 ..OptConfig::default()
472 };
473 blocked_matmul(a, b, &config)
474 }
475}
476
477#[allow(dead_code)]
479pub fn inplace_add<F>(
480 a: &mut scirs2_core::ndarray::ArrayViewMut2<F>,
481 b: &ArrayView2<F>,
482) -> LinalgResult<()>
483where
484 F: Float + NumAssign + Send + Sync,
485{
486 let mut a_owned = a.to_owned();
487 inplace::add_assign(&mut a_owned, b)?;
488 a.assign(&a_owned);
489 Ok(())
490}
491
492#[allow(dead_code)]
494pub fn inplace_scale<F>(
495 a: &mut scirs2_core::ndarray::ArrayViewMut2<F>,
496 scalar: F,
497) -> LinalgResult<()>
498where
499 F: Float + NumAssign + Send + Sync,
500{
501 for elem in a.iter_mut() {
502 *elem *= scalar;
503 }
504 Ok(())
505}
506
507#[allow(dead_code)]
509pub fn matmul_benchmark<F>(
510 a: &ArrayView2<F>,
511 b: &ArrayView2<F>,
512 config: &OptConfig,
513) -> LinalgResult<String>
514where
515 F: Float + NumAssign + Sum + Send + Sync + scirs2_core::ndarray::ScalarOperand + 'static,
516{
517 use std::time::Instant;
518
519 let start = Instant::now();
521 let _c1 = a.dot(b);
522 let time_standard = start.elapsed();
523
524 let start = Instant::now();
526 let _c2 = blocked_matmul(a, b, config)?;
527 let time_optimized = start.elapsed();
528
529 Ok(format!(
530 "Standard: {:?}, Optimized: {:?}, Speedup: {:.2}x",
531 time_standard,
532 time_optimized,
533 time_standard.as_secs_f64() / time_optimized.as_secs_f64()
534 ))
535}
536
537pub mod decomposition_opt {
540 use super::*;
541 use scirs2_core::ndarray::{s, Array1, Array2, ArrayView2};
542
543 pub struct QRWorkspace<F: Float> {
545 pub tau: Array1<F>,
547 pub work: Array1<F>,
549 pub tempmatrix: Array2<F>,
551 }
552
553 impl<F: Float> QRWorkspace<F> {
554 pub fn new(_max_rows: usize, maxcols: usize) -> Self {
556 let min_dim = _max_rows.min(maxcols);
557 Self {
558 tau: Array1::zeros(min_dim),
559 work: Array1::zeros(maxcols * 64), tempmatrix: Array2::zeros((_max_rows, maxcols)),
561 }
562 }
563
564 pub fn resize_if_needed(&mut self, rows: usize, cols: usize) {
566 let min_dim = rows.min(cols);
567
568 if self.tau.len() < min_dim {
569 self.tau = Array1::zeros(min_dim);
570 }
571
572 let worksize = cols * 64;
573 if self.work.len() < worksize {
574 self.work = Array1::zeros(worksize);
575 }
576
577 if self.tempmatrix.nrows() < rows || self.tempmatrix.ncols() < cols {
578 self.tempmatrix = Array2::zeros((rows, cols));
579 }
580 }
581 }
582
583 pub fn qr_with_workspace<F>(
586 a: &ArrayView2<F>,
587 workspace: &mut QRWorkspace<F>,
588 ) -> LinalgResult<(Array2<F>, Array2<F>)>
589 where
590 F: Float + NumAssign + Sum + Clone,
591 {
592 let (m, n) = a.dim();
593 workspace.resize_if_needed(m, n);
594
595 let mut a_work = workspace.tempmatrix.slice_mut(s![..m, ..n]);
597 a_work.assign(a);
598
599 let min_dim = m.min(n);
601
602 for j in 0..min_dim {
605 let column = a_work.column(j);
606 let norm = column
607 .slice(s![j..])
608 .fold(F::zero(), |acc, &x| acc + x * x)
609 .sqrt();
610
611 if norm > F::epsilon() {
612 workspace.tau[j] = norm;
614
615 let alpha = column[j];
617 let sign = if alpha >= F::zero() {
618 F::one()
619 } else {
620 -F::one()
621 };
622 let u1 = alpha + sign * norm;
623
624 if u1.abs() > F::epsilon() {
626 let scale = F::one() / u1;
627 for i in (j + 1)..m {
628 a_work[[i, j]] *= scale;
629 }
630 }
631 }
632 }
633
634 let q = Array2::eye(m);
636 let mut r = Array2::zeros((m, n));
637
638 for i in 0..m {
640 for j in i..n {
641 if i < min_dim && j < n {
642 r[[i, j]] = a_work[[i, j]];
643 }
644 }
645 }
646
647 Ok((q, r))
648 }
649
650 pub struct DecompositionMemoryPool<F: Float> {
652 pub arrays: Vec<Array2<F>>,
654 pub vectors: Vec<Array1<F>>,
656 pub max_poolsize: usize,
658 }
659
660 impl<F: Float> DecompositionMemoryPool<F> {
661 pub fn new(_max_poolsize: usize) -> Self {
663 Self {
664 arrays: Vec::new(),
665 vectors: Vec::new(),
666 max_poolsize: _max_poolsize,
667 }
668 }
669
670 pub fn getarray(&mut self, rows: usize, cols: usize) -> Array2<F> {
672 for (i, array) in self.arrays.iter().enumerate() {
674 if array.nrows() >= rows && array.ncols() >= cols {
675 let mut result = self.arrays.swap_remove(i);
676 result = result.slice(s![..rows, ..cols]).to_owned();
678 result.fill(F::zero()); return result;
680 }
681 }
682
683 Array2::zeros((rows, cols))
685 }
686
687 pub fn returnarray(&mut self, array: Array2<F>) {
689 if self.arrays.len() < self.max_poolsize {
690 self.arrays.push(array);
691 }
692 }
693
694 pub fn get_vector(&mut self, len: usize) -> Array1<F> {
696 for (i, vector) in self.vectors.iter().enumerate() {
698 if vector.len() >= len {
699 let mut result = self.vectors.swap_remove(i);
700 result = result.slice(s![..len]).to_owned();
701 result.fill(F::zero()); return result;
703 }
704 }
705
706 Array1::zeros(len)
708 }
709
710 pub fn return_vector(&mut self, vector: Array1<F>) {
712 if self.vectors.len() < self.max_poolsize {
713 self.vectors.push(vector);
714 }
715 }
716
717 pub fn clear(&mut self) {
719 self.arrays.clear();
720 self.vectors.clear();
721 }
722 }
723
724 pub fn blocked_qr<F>(
727 a: &ArrayView2<F>,
728 blocksize: usize,
729 ) -> LinalgResult<(Array2<F>, Array2<F>)>
730 where
731 F: Float + NumAssign + Sum + Clone,
732 {
733 let (m, n) = a.dim();
734 let mut a_copy = a.to_owned();
735 let q = Array2::eye(m);
736
737 let min_dim = m.min(n);
738
739 for start_col in (0..min_dim).step_by(blocksize) {
741 let end_col = (start_col + blocksize).min(min_dim);
742 let _panel_width = end_col - start_col;
743
744 for j in start_col..end_col {
746 let col_norm = a_copy
748 .slice(s![j.., j])
749 .fold(F::zero(), |acc, &x| acc + x * x)
750 .sqrt();
751
752 if col_norm > F::epsilon() {
753 let alpha = a_copy[[j, j]];
754 let sign = if alpha >= F::zero() {
755 F::one()
756 } else {
757 -F::one()
758 };
759 let beta = alpha + sign * col_norm;
760
761 if beta.abs() > F::epsilon() {
762 let scale = F::one() / beta;
764 for i in (j + 1)..m {
765 a_copy[[i, j]] *= scale;
766 }
767
768 a_copy[[j, j]] = -sign * col_norm;
770
771 for k in (j + 1)..n {
773 let mut dot_product = a_copy[[j, k]];
774 for i in (j + 1)..m {
775 dot_product += a_copy[[i, j]] * a_copy[[i, k]];
776 }
777
778 let tau = dot_product * F::from(2.0).expect("Operation failed");
779 a_copy[[j, k]] -= tau;
780 for i in (j + 1)..m {
781 let householder_val = a_copy[[i, j]];
782 a_copy[[i, k]] -= tau * householder_val;
783 }
784 }
785 }
786 }
787 }
788 }
789
790 let mut r = Array2::zeros((m, n));
792 for i in 0..m {
793 for j in i..n {
794 if i < min_dim {
795 r[[i, j]] = a_copy[[i, j]];
796 }
797 }
798 }
799
800 Ok((q, r))
801 }
802}
803
804#[cfg(test)]
805mod tests {
806 use super::*;
807 use approx::assert_relative_eq;
808 use scirs2_core::ndarray::array;
809
810 #[test]
811 fn test_blocked_matmul() {
812 let a = array![[1.0, 2.0], [3.0, 4.0]];
813 let b = array![[5.0, 6.0], [7.0, 8.0]];
814
815 let config = OptConfig {
816 blocksize: 1,
817 parallel_threshold: 1000,
818 num_threads: None,
819 algorithm: OptAlgorithm::Blocked,
820 };
821
822 let c = blocked_matmul(&a.view(), &b.view(), &config).expect("Operation failed");
823
824 assert_relative_eq!(c[[0, 0]], 19.0);
825 assert_relative_eq!(c[[0, 1]], 22.0);
826 assert_relative_eq!(c[[1, 0]], 43.0);
827 assert_relative_eq!(c[[1, 1]], 50.0);
828 }
829
830 #[test]
831 fn test_optimized_transpose() {
832 let a = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
833 let a_t = optimized_transpose(&a.view()).expect("Operation failed");
834
835 assert_eq!(a_t.shape(), &[3, 2]);
836 assert_relative_eq!(a_t[[0, 0]], 1.0);
837 assert_relative_eq!(a_t[[1, 0]], 2.0);
838 assert_relative_eq!(a_t[[2, 0]], 3.0);
839 assert_relative_eq!(a_t[[0, 1]], 4.0);
840 assert_relative_eq!(a_t[[1, 1]], 5.0);
841 assert_relative_eq!(a_t[[2, 1]], 6.0);
842 }
843
844 #[test]
845 fn test_parallel_matvec() {
846 let a = array![[1.0, 2.0], [3.0, 4.0]];
847 let x = array![[5.0], [6.0]];
848
849 let config = OptConfig::default();
850 let y = parallel_matvec(&a.view(), &x.view(), &config).expect("Operation failed");
851
852 assert_relative_eq!(y[[0, 0]], 17.0);
853 assert_relative_eq!(y[[1, 0]], 39.0);
854 }
855
856 #[test]
857 fn test_inplace_operations() {
858 let mut a = array![[1.0, 2.0], [3.0, 4.0]];
859 let b = array![[5.0, 6.0], [7.0, 8.0]];
860
861 inplace::add_assign(&mut a, &b.view()).expect("Operation failed");
862
863 assert_relative_eq!(a[[0, 0]], 6.0);
864 assert_relative_eq!(a[[0, 1]], 8.0);
865 assert_relative_eq!(a[[1, 0]], 10.0);
866 assert_relative_eq!(a[[1, 1]], 12.0);
867
868 inplace::scalar_mul_assign(&mut a, 2.0).expect("Operation failed");
869
870 assert_relative_eq!(a[[0, 0]], 12.0);
871 assert_relative_eq!(a[[0, 1]], 16.0);
872 assert_relative_eq!(a[[1, 0]], 20.0);
873 assert_relative_eq!(a[[1, 1]], 24.0);
874 }
875
876 #[test]
877 fn test_inplace_transpose() {
878 let mut a = array![[1.0, 2.0], [3.0, 4.0]];
879
880 inplace::transpose_square(&mut a).expect("Operation failed");
881
882 assert_relative_eq!(a[[0, 0]], 1.0);
883 assert_relative_eq!(a[[0, 1]], 3.0);
884 assert_relative_eq!(a[[1, 0]], 2.0);
885 assert_relative_eq!(a[[1, 1]], 4.0);
886 }
887
888 #[test]
889 fn test_adaptive_matmul() {
890 let a = array![[1.0, 2.0], [3.0, 4.0]];
891 let b = array![[5.0, 6.0], [7.0, 8.0]];
892
893 let c = adaptive_matmul(&a.view(), &b.view()).expect("Operation failed");
894
895 assert_relative_eq!(c[[0, 0]], 19.0);
896 assert_relative_eq!(c[[0, 1]], 22.0);
897 assert_relative_eq!(c[[1, 0]], 43.0);
898 assert_relative_eq!(c[[1, 1]], 50.0);
899 }
900
901 #[test]
902 fn test_largematrix_blocked() {
903 let n = 100;
905 let a = Array2::from_shape_fn((n, n), |(i, j)| (i + j) as f64);
906 let b = Array2::eye(n);
907
908 let config = OptConfig {
909 blocksize: 16,
910 parallel_threshold: 10000,
911 num_threads: None,
912 algorithm: OptAlgorithm::Blocked,
913 };
914
915 let c = blocked_matmul(&a.view(), &b.view(), &config).expect("Operation failed");
916
917 for i in 0..n {
919 for j in 0..n {
920 assert_relative_eq!(c[[i, j]], a[[i, j]], epsilon = 1e-10);
921 }
922 }
923 }
924}