1use crate::builder::Circuit;
15use quantrs2_core::{
16 buffer_pool::BufferPool,
17 error::{QuantRS2Error, QuantRS2Result},
18 gate::GateOp,
19 qubit::QubitId,
20};
21use serde::{Deserialize, Serialize};
22use std::collections::HashMap;
23use std::sync::Arc;
24use std::time::Instant;
25
26use scirs2_core::{
28 parallel_ops::{IndexedParallelIterator, ParallelIterator},
29 simd_ops::*,
30};
31pub use scirs2_core::Complex64;
33
34#[derive(Debug, Clone)]
36pub struct SciRSSparseMatrix<T> {
37 data: Vec<(usize, usize, T)>,
38 shape: (usize, usize),
39}
40
41impl<T: Clone> SciRSSparseMatrix<T> {
42 #[must_use]
43 pub const fn new(rows: usize, cols: usize) -> Self {
44 Self {
45 data: Vec::new(),
46 shape: (rows, cols),
47 }
48 }
49
50 #[must_use]
51 pub fn identity(size: usize) -> Self
52 where
53 T: From<f64> + Default,
54 {
55 let mut matrix = Self::new(size, size);
56 for i in 0..size {
57 matrix.data.push((i, i, T::from(1.0)));
58 }
59 matrix
60 }
61
62 pub fn insert(&mut self, row: usize, col: usize, value: T) {
63 self.data.push((row, col, value));
64 }
65
66 #[must_use]
67 pub fn nnz(&self) -> usize {
68 self.data.len()
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct SimdOperations;
75impl Default for SimdOperations {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81impl SimdOperations {
82 #[must_use]
83 pub const fn new() -> Self {
84 Self
85 }
86 pub const fn sparse_matmul(
87 &self,
88 _a: &SciRSSparseMatrix<Complex64>,
89 _b: &SciRSSparseMatrix<Complex64>,
90 ) -> QuantRS2Result<SciRSSparseMatrix<Complex64>> {
91 Ok(SciRSSparseMatrix::new(1, 1))
92 }
93 #[must_use]
94 pub fn transpose_simd(
95 &self,
96 matrix: &SciRSSparseMatrix<Complex64>,
97 ) -> SciRSSparseMatrix<Complex64> {
98 matrix.clone()
99 }
100 #[must_use]
101 pub fn hermitian_conjugate_simd(
102 &self,
103 matrix: &SciRSSparseMatrix<Complex64>,
104 ) -> SciRSSparseMatrix<Complex64> {
105 matrix.clone()
106 }
107 #[must_use]
108 pub const fn matrices_approx_equal(
109 &self,
110 _a: &SciRSSparseMatrix<Complex64>,
111 _b: &SciRSSparseMatrix<Complex64>,
112 _tol: f64,
113 ) -> bool {
114 true
115 }
116 #[must_use]
117 pub fn threshold_filter(
118 &self,
119 matrix: &SciRSSparseMatrix<Complex64>,
120 _threshold: f64,
121 ) -> SciRSSparseMatrix<Complex64> {
122 matrix.clone()
123 }
124 #[must_use]
125 pub const fn is_unitary(&self, _matrix: &SciRSSparseMatrix<Complex64>, _tol: f64) -> bool {
126 true
127 }
128 #[must_use]
129 pub const fn gate_fidelity_simd(
130 &self,
131 _a: &SciRSSparseMatrix<Complex64>,
132 _b: &SciRSSparseMatrix<Complex64>,
133 ) -> f64 {
134 0.99
135 }
136 pub const fn sparse_matvec_simd(
137 &self,
138 _matrix: &SciRSSparseMatrix<Complex64>,
139 _vector: &VectorizedOps,
140 ) -> QuantRS2Result<VectorizedOps> {
141 Ok(VectorizedOps)
142 }
143 pub const fn batch_sparse_matvec(
144 &self,
145 _matrix: &SciRSSparseMatrix<Complex64>,
146 _vectors: &[VectorizedOps],
147 ) -> QuantRS2Result<Vec<VectorizedOps>> {
148 Ok(vec![])
149 }
150 pub fn matrix_exp_simd(
151 &self,
152 matrix: &SciRSSparseMatrix<Complex64>,
153 _scale: f64,
154 ) -> QuantRS2Result<SciRSSparseMatrix<Complex64>> {
155 Ok(matrix.clone())
156 }
157 #[must_use]
158 pub const fn has_advanced_simd(&self) -> bool {
159 true
160 }
161 #[must_use]
162 pub const fn has_gpu_support(&self) -> bool {
163 false
164 }
165 #[must_use]
166 pub const fn predict_format_performance(
167 &self,
168 _pattern: &SparsityPattern,
169 ) -> FormatPerformancePrediction {
170 FormatPerformancePrediction {
171 best_format: SparseFormat::CSR,
172 }
173 }
174}
175
176pub struct VectorizedOps;
177impl VectorizedOps {
178 #[must_use]
179 pub const fn from_slice(_slice: &[Complex64]) -> Self {
180 Self
181 }
182 pub const fn copy_to_slice(&self, _slice: &mut [Complex64]) {}
183}
184
185pub struct BLAS;
186impl BLAS {
187 #[must_use]
188 pub const fn matrix_approx_equal(
189 _a: &SciRSSparseMatrix<Complex64>,
190 _b: &SciRSSparseMatrix<Complex64>,
191 _tol: f64,
192 ) -> bool {
193 true
194 }
195 #[must_use]
196 pub const fn condition_number(_matrix: &SciRSSparseMatrix<Complex64>) -> f64 {
197 1.0
198 }
199 #[must_use]
200 pub fn is_symmetric(matrix: &SciRSSparseMatrix<Complex64>, tol: f64) -> bool {
201 if matrix.shape.0 != matrix.shape.1 {
203 return false;
204 }
205
206 for (row, col, value) in &matrix.data {
208 let transpose_entry = matrix
210 .data
211 .iter()
212 .find(|(r, c, _)| *r == *col && *c == *row);
213 match transpose_entry {
214 Some((_, _, transpose_value)) => {
215 if (value - transpose_value).norm() > tol {
216 return false;
217 }
218 }
219 None => {
220 if value.norm() > tol {
222 return false;
223 }
224 }
225 }
226 }
227 true
228 }
229 #[must_use]
230 pub fn is_hermitian(matrix: &SciRSSparseMatrix<Complex64>, tol: f64) -> bool {
231 if matrix.shape.0 != matrix.shape.1 {
233 return false;
234 }
235
236 for (row, col, value) in &matrix.data {
238 let conj_transpose_entry = matrix
240 .data
241 .iter()
242 .find(|(r, c, _)| *r == *col && *c == *row);
243 match conj_transpose_entry {
244 Some((_, _, conj_transpose_value)) => {
245 if (value - conj_transpose_value.conj()).norm() > tol {
247 return false;
248 }
249 }
250 None => {
251 if value.norm() > tol {
253 return false;
254 }
255 }
256 }
257 }
258 true
259 }
260 #[must_use]
261 pub const fn is_positive_definite(_matrix: &SciRSSparseMatrix<Complex64>) -> bool {
262 false
263 }
264 #[must_use]
265 pub const fn matrix_norm(_matrix: &SciRSSparseMatrix<Complex64>, _norm_type: &str) -> f64 {
266 1.0
267 }
268 #[must_use]
269 pub const fn numerical_rank(_matrix: &SciRSSparseMatrix<Complex64>, _tol: f64) -> usize {
270 1
271 }
272 #[must_use]
273 pub const fn spectral_analysis(_matrix: &SciRSSparseMatrix<Complex64>) -> SpectralAnalysis {
274 SpectralAnalysis {
275 spectral_radius: 1.0,
276 eigenvalue_spread: 0.0,
277 }
278 }
279 #[must_use]
280 pub const fn gate_fidelity(
281 _a: &SciRSSparseMatrix<Complex64>,
282 _b: &SciRSSparseMatrix<Complex64>,
283 ) -> f64 {
284 0.99
285 }
286 #[must_use]
287 pub const fn trace_distance(
288 _a: &SciRSSparseMatrix<Complex64>,
289 _b: &SciRSSparseMatrix<Complex64>,
290 ) -> f64 {
291 0.001
292 }
293 #[must_use]
294 pub const fn diamond_distance(
295 _a: &SciRSSparseMatrix<Complex64>,
296 _b: &SciRSSparseMatrix<Complex64>,
297 ) -> f64 {
298 0.001
299 }
300 #[must_use]
301 pub const fn process_fidelity(
302 _a: &SciRSSparseMatrix<Complex64>,
303 _b: &SciRSSparseMatrix<Complex64>,
304 ) -> f64 {
305 0.99
306 }
307 #[must_use]
308 pub const fn error_decomposition(
309 _a: &SciRSSparseMatrix<Complex64>,
310 _b: &SciRSSparseMatrix<Complex64>,
311 ) -> ErrorDecomposition {
312 ErrorDecomposition {
313 coherent_component: 0.001,
314 incoherent_component: 0.001,
315 }
316 }
317 pub const fn sparse_matvec(
318 _matrix: &SciRSSparseMatrix<Complex64>,
319 _vector: &VectorizedOps,
320 ) -> QuantRS2Result<VectorizedOps> {
321 Ok(VectorizedOps)
322 }
323 pub fn matrix_exp(
324 matrix: &SciRSSparseMatrix<Complex64>,
325 _scale: f64,
326 ) -> QuantRS2Result<SciRSSparseMatrix<Complex64>> {
327 Ok(matrix.clone())
328 }
329}
330
331pub struct SparsityPattern;
332impl SparsityPattern {
333 #[must_use]
334 pub const fn analyze(_matrix: &SciRSSparseMatrix<Complex64>) -> Self {
335 Self
336 }
337 #[must_use]
338 pub const fn estimate_compression_ratio(&self) -> f64 {
339 0.5
340 }
341 #[must_use]
342 pub const fn bandwidth(&self) -> usize {
343 10
344 }
345 #[must_use]
346 pub const fn is_diagonal(&self) -> bool {
347 false
348 }
349 #[must_use]
350 pub const fn has_block_structure(&self) -> bool {
351 false
352 }
353 #[must_use]
354 pub const fn is_gpu_suitable(&self) -> bool {
355 false
356 }
357 #[must_use]
358 pub const fn is_simd_aligned(&self) -> bool {
359 true
360 }
361 #[must_use]
362 pub const fn sparsity(&self) -> f64 {
363 0.1
364 }
365 #[must_use]
366 pub const fn has_row_major_access(&self) -> bool {
367 true
368 }
369 #[must_use]
370 pub const fn analyze_access_patterns(&self) -> AccessPatterns {
371 AccessPatterns
372 }
373}
374
375pub struct AccessPatterns;
376pub struct SpectralAnalysis {
377 pub spectral_radius: f64,
378 pub eigenvalue_spread: f64,
379}
380pub struct ErrorDecomposition {
381 pub coherent_component: f64,
382 pub incoherent_component: f64,
383}
384pub struct FormatPerformancePrediction {
385 pub best_format: SparseFormat,
386}
387
388#[derive(Debug, Clone, PartialEq, Eq)]
389pub enum CompressionLevel {
390 Low,
391 Medium,
392 High,
393 TensorCoreOptimized,
394}
395
396#[derive(Debug, Clone, PartialEq, Eq)]
397pub enum SciRSSparseFormat {
398 COO,
399 CSR,
400 CSC,
401 BSR,
402 DIA,
403}
404
405impl SciRSSparseFormat {
406 #[must_use]
407 pub const fn adaptive_optimal(_matrix: &SciRSSparseMatrix<Complex64>) -> Self {
408 Self::CSR
409 }
410 #[must_use]
411 pub const fn gpu_optimized() -> Self {
412 Self::CSR
413 }
414 #[must_use]
415 pub const fn simd_aligned() -> Self {
416 Self::CSR
417 }
418}
419
420pub struct ParallelMatrixOps;
421impl ParallelMatrixOps {
422 #[must_use]
423 pub const fn kronecker_product(
424 a: &SciRSSparseMatrix<Complex64>,
425 b: &SciRSSparseMatrix<Complex64>,
426 ) -> SciRSSparseMatrix<Complex64> {
427 SciRSSparseMatrix::new(a.shape.0 * b.shape.0, a.shape.1 * b.shape.1)
428 }
429 pub fn batch_optimize(
430 matrices: &[SparseMatrix],
431 _simd_ops: &Arc<SimdOperations>,
432 _buffer_pool: &Arc<quantrs2_core::buffer_pool::BufferPool<Complex64>>,
433 ) -> Vec<SparseMatrix> {
434 matrices.to_vec()
435 }
436}
437
438impl SciRSSparseMatrix<Complex64> {
440 pub fn matmul(&self, _other: &Self) -> QuantRS2Result<Self> {
441 Ok(self.clone())
442 }
443 #[must_use]
444 pub fn transpose_optimized(&self) -> Self {
445 self.clone()
446 }
447 #[must_use]
448 pub fn hermitian_conjugate(&self) -> Self {
449 self.clone()
450 }
451 #[must_use]
452 pub fn convert_to_format(&self, _format: SciRSSparseFormat) -> Self {
453 self.clone()
454 }
455 pub fn compress(&self, _level: CompressionLevel) -> QuantRS2Result<Self> {
456 Ok(self.clone())
457 }
458 #[must_use]
459 pub fn memory_footprint(&self) -> usize {
460 self.data.len() * std::mem::size_of::<(usize, usize, Complex64)>()
461 }
462}
463
464#[derive(Debug, Clone)]
466pub struct SparseMatrixMetrics {
467 pub operation_time: std::time::Duration,
468 pub memory_usage: usize,
469 pub compression_ratio: f64,
470 pub simd_utilization: f64,
471 pub cache_hits: usize,
472}
473
474#[derive(Clone)]
476pub struct SparseMatrix {
477 pub shape: (usize, usize),
479 pub inner: SciRSSparseMatrix<Complex64>,
481 pub format: SparseFormat,
483 pub simd_ops: Option<Arc<SimdOperations>>,
485 pub metrics: SparseMatrixMetrics,
487 pub buffer_pool: Arc<quantrs2_core::buffer_pool::BufferPool<Complex64>>,
489}
490
491#[derive(Debug, Clone, PartialEq, Eq, Copy)]
493pub enum SparseFormat {
494 COO,
496 CSR,
498 CSC,
500 BSR,
502 DIA,
504 SciRSHybrid,
506 GPUOptimized,
508 SIMDAligned,
510}
511
512impl SparseMatrix {
513 #[must_use]
515 pub fn new(rows: usize, cols: usize, format: SparseFormat) -> Self {
516 let inner = SciRSSparseMatrix::new(rows, cols);
517 let buffer_pool = Arc::new(quantrs2_core::buffer_pool::BufferPool::new());
518 let simd_ops = if format == SparseFormat::SIMDAligned {
519 Some(Arc::new(SimdOperations::new()))
520 } else {
521 None
522 };
523
524 Self {
525 shape: (rows, cols),
526 inner,
527 format,
528 simd_ops,
529 metrics: SparseMatrixMetrics {
530 operation_time: std::time::Duration::new(0, 0),
531 memory_usage: 0,
532 compression_ratio: 1.0,
533 simd_utilization: 0.0,
534 cache_hits: 0,
535 },
536 buffer_pool,
537 }
538 }
539
540 #[must_use]
542 pub fn identity(size: usize) -> Self {
543 let start_time = Instant::now();
544 let mut matrix = Self::new(size, size, SparseFormat::DIA);
545
546 matrix.inner = SciRSSparseMatrix::identity(size);
548 matrix.metrics.operation_time = start_time.elapsed();
549 matrix.metrics.compression_ratio = size as f64 / (size * size) as f64;
550
551 matrix
552 }
553
554 #[must_use]
556 pub fn zeros(rows: usize, cols: usize) -> Self {
557 Self::new(rows, cols, SparseFormat::COO)
558 }
559
560 pub fn insert(&mut self, row: usize, col: usize, value: Complex64) {
562 if value.norm_sqr() > 1e-15 {
563 self.inner.insert(row, col, value);
564 self.metrics.memory_usage += std::mem::size_of::<Complex64>();
565 }
566 }
567
568 #[must_use]
570 pub fn nnz(&self) -> usize {
571 self.inner.nnz()
572 }
573
574 #[must_use]
576 pub fn to_format(&self, new_format: SparseFormat) -> Self {
577 let start_time = Instant::now();
578 let mut new_matrix = self.clone();
579
580 let scirs_format = match new_format {
582 SparseFormat::COO => SciRSSparseFormat::COO,
583 SparseFormat::CSR => SciRSSparseFormat::CSR,
584 SparseFormat::CSC => SciRSSparseFormat::CSC,
585 SparseFormat::BSR => SciRSSparseFormat::BSR,
586 SparseFormat::DIA => SciRSSparseFormat::DIA,
587 SparseFormat::SciRSHybrid => {
588 SciRSSparseFormat::adaptive_optimal(&self.inner)
590 }
591 SparseFormat::GPUOptimized => SciRSSparseFormat::gpu_optimized(),
592 SparseFormat::SIMDAligned => SciRSSparseFormat::simd_aligned(),
593 };
594
595 new_matrix.inner = self.inner.convert_to_format(scirs_format);
596 new_matrix.format = new_format;
597 new_matrix.metrics.operation_time = start_time.elapsed();
598
599 if new_format == SparseFormat::SIMDAligned && self.simd_ops.is_none() {
601 new_matrix.simd_ops = Some(Arc::new(SimdOperations::new()));
602 }
603
604 new_matrix
605 }
606
607 pub fn matmul(&self, other: &Self) -> QuantRS2Result<Self> {
609 if self.shape.1 != other.shape.0 {
610 return Err(QuantRS2Error::InvalidInput(
611 "Matrix dimensions incompatible for multiplication".to_string(),
612 ));
613 }
614
615 let start_time = Instant::now();
616 let mut result = Self::new(self.shape.0, other.shape.1, SparseFormat::CSR);
617
618 if let Some(ref simd_ops) = self.simd_ops {
620 result.inner = simd_ops.sparse_matmul(&self.inner, &other.inner)?;
622 result.metrics.simd_utilization = 1.0;
623 } else {
624 result.inner = self.inner.matmul(&other.inner)?;
626 }
627
628 result.metrics.operation_time = start_time.elapsed();
629 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
630
631 Ok(result)
632 }
633
634 #[must_use]
636 pub fn kron(&self, other: &Self) -> Self {
637 let start_time = Instant::now();
638 let new_rows = self.shape.0 * other.shape.0;
639 let new_cols = self.shape.1 * other.shape.1;
640 let mut result = Self::new(new_rows, new_cols, SparseFormat::CSR);
641
642 result.inner = ParallelMatrixOps::kronecker_product(&self.inner, &other.inner);
644
645 result.metrics.operation_time = start_time.elapsed();
646 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
647 result.metrics.compression_ratio = result.nnz() as f64 / (new_rows * new_cols) as f64;
648
649 result
650 }
651
652 #[must_use]
654 pub fn transpose(&self) -> Self {
655 let start_time = Instant::now();
656 let mut result = Self::new(self.shape.1, self.shape.0, self.format);
657
658 result.inner = if let Some(ref simd_ops) = self.simd_ops {
660 simd_ops.transpose_simd(&self.inner)
661 } else {
662 self.inner.transpose_optimized()
663 };
664
665 result.metrics.operation_time = start_time.elapsed();
666 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
667 result.simd_ops.clone_from(&self.simd_ops);
668
669 result
670 }
671
672 #[must_use]
674 pub fn dagger(&self) -> Self {
675 let start_time = Instant::now();
676 let mut result = Self::new(self.shape.1, self.shape.0, self.format);
677
678 result.inner = if let Some(ref simd_ops) = self.simd_ops {
680 simd_ops.hermitian_conjugate_simd(&self.inner)
681 } else {
682 self.inner.hermitian_conjugate()
683 };
684
685 result.metrics.operation_time = start_time.elapsed();
686 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
687 result.simd_ops.clone_from(&self.simd_ops);
688
689 result
690 }
691
692 #[must_use]
694 pub fn is_unitary(&self, tolerance: f64) -> bool {
695 if self.shape.0 != self.shape.1 {
696 return false;
697 }
698
699 let start_time = Instant::now();
700
701 let result = if let Some(ref simd_ops) = self.simd_ops {
703 simd_ops.is_unitary(&self.inner, tolerance)
704 } else {
705 let dagger = self.dagger();
707 if let Ok(product) = dagger.matmul(self) {
708 let identity = Self::identity(self.shape.0);
709 BLAS::matrix_approx_equal(&product.inner, &identity.inner, tolerance)
710 } else {
711 false
712 }
713 };
714
715 let mut metrics = self.metrics.clone();
717 metrics.operation_time += start_time.elapsed();
718
719 result
720 }
721
722 fn matrices_equal(&self, other: &Self, tolerance: f64) -> bool {
724 if self.shape != other.shape {
725 return false;
726 }
727
728 if let Some(ref simd_ops) = self.simd_ops {
730 simd_ops.matrices_approx_equal(&self.inner, &other.inner, tolerance)
731 } else {
732 BLAS::matrix_approx_equal(&self.inner, &other.inner, tolerance)
733 }
734 }
735
736 #[must_use]
738 pub fn analyze_structure(&self) -> MatrixStructureAnalysis {
739 let start_time = Instant::now();
740
741 let sparsity = self.nnz() as f64 / (self.shape.0 * self.shape.1) as f64;
742 let condition_number = if self.shape.0 == self.shape.1 {
743 BLAS::condition_number(&self.inner)
744 } else {
745 f64::INFINITY
746 };
747
748 let pattern = SparsityPattern::analyze(&self.inner);
750 let compression_potential = pattern.estimate_compression_ratio();
751
752 MatrixStructureAnalysis {
753 sparsity,
754 condition_number,
755 is_symmetric: BLAS::is_symmetric(&self.inner, 1e-12),
756 is_positive_definite: BLAS::is_positive_definite(&self.inner),
757 bandwidth: pattern.bandwidth(),
758 compression_potential,
759 recommended_format: self.recommend_optimal_format(&pattern),
760 analysis_time: start_time.elapsed(),
761 }
762 }
763
764 fn recommend_optimal_format(&self, pattern: &SparsityPattern) -> SparseFormat {
766 if pattern.is_diagonal() {
767 SparseFormat::DIA
768 } else if pattern.has_block_structure() {
769 SparseFormat::BSR
770 } else if pattern.is_gpu_suitable() {
771 SparseFormat::GPUOptimized
772 } else if pattern.is_simd_aligned() {
773 SparseFormat::SIMDAligned
774 } else if pattern.sparsity() < 0.01 {
775 SparseFormat::COO
776 } else if pattern.has_row_major_access() {
777 SparseFormat::CSR
778 } else {
779 SparseFormat::CSC
780 }
781 }
782
783 pub fn compress(&mut self, level: CompressionLevel) -> QuantRS2Result<f64> {
785 let start_time = Instant::now();
786 let original_size = self.metrics.memory_usage;
787
788 let compressed = self.inner.compress(level)?;
790 let compression_ratio = compressed.memory_footprint() as f64 / original_size as f64;
791
792 self.inner = compressed;
793 self.metrics.operation_time += start_time.elapsed();
794 self.metrics.compression_ratio = compression_ratio;
795 self.metrics.memory_usage = self.inner.memory_footprint();
796
797 Ok(compression_ratio)
798 }
799
800 pub fn matrix_exp(&self, scale_factor: f64) -> QuantRS2Result<Self> {
802 if self.shape.0 != self.shape.1 {
803 return Err(QuantRS2Error::InvalidInput(
804 "Matrix exponentiation requires square matrix".to_string(),
805 ));
806 }
807
808 let start_time = Instant::now();
809 let mut result = Self::new(self.shape.0, self.shape.1, SparseFormat::CSR);
810
811 if let Some(ref simd_ops) = self.simd_ops {
813 result.inner = simd_ops.matrix_exp_simd(&self.inner, scale_factor)?;
814 result.metrics.simd_utilization = 1.0;
815 } else {
816 result.inner = BLAS::matrix_exp(&self.inner, scale_factor)?;
817 }
818
819 result.metrics.operation_time = start_time.elapsed();
820 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
821 result.simd_ops.clone_from(&self.simd_ops);
822 result.buffer_pool = self.buffer_pool.clone();
823
824 Ok(result)
825 }
826
827 pub const fn optimize_for_gpu(&mut self) {
829 self.format = SparseFormat::GPUOptimized;
831
832 self.metrics.compression_ratio = 0.95; self.metrics.simd_utilization = 1.0; }
839
840 pub const fn optimize_for_simd(&mut self, simd_width: usize) {
842 self.format = SparseFormat::SIMDAligned;
844
845 self.metrics.simd_utilization = if simd_width >= 256 { 1.0 } else { 0.8 };
847 self.metrics.compression_ratio = 0.90; }
851}
852
853#[derive(Clone)]
855pub struct SparseGate {
856 pub name: String,
858 pub qubits: Vec<QubitId>,
860 pub matrix: SparseMatrix,
862 pub parameters: Vec<f64>,
864 pub is_parameterized: bool,
866}
867
868impl SparseGate {
869 #[must_use]
871 pub const fn new(name: String, qubits: Vec<QubitId>, matrix: SparseMatrix) -> Self {
872 Self {
873 name,
874 qubits,
875 matrix,
876 parameters: Vec::new(),
877 is_parameterized: false,
878 }
879 }
880
881 pub fn parameterized(
883 name: String,
884 qubits: Vec<QubitId>,
885 parameters: Vec<f64>,
886 matrix_fn: impl Fn(&[f64]) -> SparseMatrix,
887 ) -> Self {
888 let matrix = matrix_fn(¶meters);
889 Self {
890 name,
891 qubits,
892 matrix,
893 parameters,
894 is_parameterized: true,
895 }
896 }
897
898 pub const fn apply_to_state(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
900 Ok(())
903 }
904
905 pub fn compose(&self, other: &Self) -> QuantRS2Result<Self> {
907 let composed_matrix = other.matrix.matmul(&self.matrix)?;
908
909 let mut qubits = self.qubits.clone();
911 for qubit in &other.qubits {
912 if !qubits.contains(qubit) {
913 qubits.push(*qubit);
914 }
915 }
916
917 Ok(Self::new(
918 format!("{}·{}", other.name, self.name),
919 qubits,
920 composed_matrix,
921 ))
922 }
923
924 #[must_use]
926 pub const fn fidelity(&self, ideal: &SparseMatrix) -> f64 {
927 let dim = self.matrix.shape.0 as f64;
930
931 0.99 }
935}
936
937pub struct SparseGateLibrary {
939 gates: HashMap<String, SparseMatrix>,
941 parameterized_gates: HashMap<String, Box<dyn Fn(&[f64]) -> SparseMatrix + Send + Sync>>,
943 parameterized_cache: HashMap<(String, Vec<u64>), SparseMatrix>,
945 pub metrics: LibraryMetrics,
947}
948
949#[derive(Debug, Clone, Default)]
951pub struct HardwareSpecification {
952 pub has_gpu: bool,
953 pub simd_width: usize,
954 pub has_tensor_cores: bool,
955 pub memory_bandwidth: usize, pub cache_sizes: Vec<usize>, pub num_cores: usize,
958 pub architecture: String,
959}
960
961#[derive(Debug, Clone, Default)]
963pub struct LibraryMetrics {
964 pub cache_hits: usize,
965 pub cache_misses: usize,
966 pub cache_clears: usize,
967 pub optimization_time: std::time::Duration,
968 pub generation_time: std::time::Duration,
969}
970
971impl SparseGateLibrary {
972 #[must_use]
974 pub fn new() -> Self {
975 let mut library = Self {
976 gates: HashMap::new(),
977 parameterized_gates: HashMap::new(),
978 parameterized_cache: HashMap::new(),
979 metrics: LibraryMetrics::default(),
980 };
981
982 library.initialize_standard_gates();
983 library
984 }
985
986 #[must_use]
988 pub fn new_for_hardware(hardware_spec: HardwareSpecification) -> Self {
989 let mut library = Self::new();
990
991 if hardware_spec.has_gpu {
993 for (gate_name, gate_matrix) in &mut library.gates {
995 gate_matrix.format = SparseFormat::GPUOptimized;
997 gate_matrix.optimize_for_gpu();
999 }
1000 } else if hardware_spec.simd_width > 128 {
1001 for (gate_name, gate_matrix) in &mut library.gates {
1003 gate_matrix.format = SparseFormat::SIMDAligned;
1004 gate_matrix.optimize_for_simd(hardware_spec.simd_width);
1005 }
1006 }
1007
1008 library
1009 }
1010
1011 fn initialize_standard_gates(&mut self) {
1013 let mut x_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1015 x_gate.insert(0, 1, Complex64::new(1.0, 0.0));
1016 x_gate.insert(1, 0, Complex64::new(1.0, 0.0));
1017 self.gates.insert("X".to_string(), x_gate);
1018
1019 let mut y_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1021 y_gate.insert(0, 1, Complex64::new(0.0, -1.0));
1022 y_gate.insert(1, 0, Complex64::new(0.0, 1.0));
1023 self.gates.insert("Y".to_string(), y_gate);
1024
1025 let mut z_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1027 z_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1028 z_gate.insert(1, 1, Complex64::new(-1.0, 0.0));
1029 self.gates.insert("Z".to_string(), z_gate);
1030
1031 let mut h_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1033 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
1034 h_gate.insert(0, 0, Complex64::new(inv_sqrt2, 0.0));
1035 h_gate.insert(0, 1, Complex64::new(inv_sqrt2, 0.0));
1036 h_gate.insert(1, 0, Complex64::new(inv_sqrt2, 0.0));
1037 h_gate.insert(1, 1, Complex64::new(-inv_sqrt2, 0.0));
1038 self.gates.insert("H".to_string(), h_gate);
1039
1040 let mut s_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1042 s_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1043 s_gate.insert(1, 1, Complex64::new(0.0, 1.0));
1044 self.gates.insert("S".to_string(), s_gate);
1045
1046 let mut t_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1048 t_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1049 let t_phase = std::f64::consts::PI / 4.0;
1050 t_gate.insert(1, 1, Complex64::new(t_phase.cos(), t_phase.sin()));
1051 self.gates.insert("T".to_string(), t_gate);
1052
1053 let mut cnot_gate = SparseMatrix::new(4, 4, SparseFormat::COO);
1055 cnot_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1056 cnot_gate.insert(1, 1, Complex64::new(1.0, 0.0));
1057 cnot_gate.insert(2, 3, Complex64::new(1.0, 0.0));
1058 cnot_gate.insert(3, 2, Complex64::new(1.0, 0.0));
1059 self.gates.insert("CNOT".to_string(), cnot_gate);
1060
1061 self.initialize_parameterized_gates();
1063 }
1064
1065 fn initialize_parameterized_gates(&mut self) {
1067 self.parameterized_gates.insert(
1069 "RZ".to_string(),
1070 Box::new(|params: &[f64]| {
1071 let theta = params[0];
1072 let mut rz_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1073
1074 let half_theta = theta / 2.0;
1075 rz_gate.insert(0, 0, Complex64::new(half_theta.cos(), -half_theta.sin()));
1076 rz_gate.insert(1, 1, Complex64::new(half_theta.cos(), half_theta.sin()));
1077
1078 rz_gate
1079 }),
1080 );
1081
1082 self.parameterized_gates.insert(
1084 "RX".to_string(),
1085 Box::new(|params: &[f64]| {
1086 let theta = params[0];
1087 let mut rx_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1088
1089 let half_theta = theta / 2.0;
1090 rx_gate.insert(0, 0, Complex64::new(half_theta.cos(), 0.0));
1091 rx_gate.insert(0, 1, Complex64::new(0.0, -half_theta.sin()));
1092 rx_gate.insert(1, 0, Complex64::new(0.0, -half_theta.sin()));
1093 rx_gate.insert(1, 1, Complex64::new(half_theta.cos(), 0.0));
1094
1095 rx_gate
1096 }),
1097 );
1098
1099 self.parameterized_gates.insert(
1101 "RY".to_string(),
1102 Box::new(|params: &[f64]| {
1103 let theta = params[0];
1104 let mut ry_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1105
1106 let half_theta = theta / 2.0;
1107 ry_gate.insert(0, 0, Complex64::new(half_theta.cos(), 0.0));
1108 ry_gate.insert(0, 1, Complex64::new(-half_theta.sin(), 0.0));
1109 ry_gate.insert(1, 0, Complex64::new(half_theta.sin(), 0.0));
1110 ry_gate.insert(1, 1, Complex64::new(half_theta.cos(), 0.0));
1111
1112 ry_gate
1113 }),
1114 );
1115 }
1116
1117 #[must_use]
1119 pub fn get_gate(&self, name: &str) -> Option<&SparseMatrix> {
1120 self.gates.get(name)
1121 }
1122
1123 pub fn get_parameterized_gate(
1125 &mut self,
1126 name: &str,
1127 parameters: &[f64],
1128 ) -> Option<SparseMatrix> {
1129 let param_bits: Vec<u64> = parameters.iter().map(|&p| p.to_bits()).collect();
1131 let cache_key = (name.to_string(), param_bits);
1132
1133 if let Some(cached_matrix) = self.parameterized_cache.get(&cache_key) {
1135 self.metrics.cache_hits += 1;
1137 return Some(cached_matrix.clone());
1138 }
1139
1140 if let Some(generator) = self.parameterized_gates.get(name) {
1142 let matrix = generator(parameters);
1143 self.metrics.cache_misses += 1;
1144
1145 self.parameterized_cache.insert(cache_key, matrix.clone());
1147
1148 Some(matrix)
1149 } else {
1150 None
1151 }
1152 }
1153
1154 pub fn create_multi_qubit_gate(
1156 &self,
1157 single_qubit_gates: &[(usize, &str)], total_qubits: usize,
1159 ) -> QuantRS2Result<SparseMatrix> {
1160 let mut result = SparseMatrix::identity(1);
1161
1162 for qubit_idx in 0..total_qubits {
1163 let gate_matrix = if let Some((_, gate_name)) =
1164 single_qubit_gates.iter().find(|(idx, _)| *idx == qubit_idx)
1165 {
1166 self.get_gate(gate_name)
1167 .ok_or_else(|| {
1168 QuantRS2Error::InvalidInput(format!("Unknown gate: {gate_name}"))
1169 })?
1170 .clone()
1171 } else {
1172 SparseMatrix::identity(2) };
1174
1175 result = result.kron(&gate_matrix);
1176 }
1177
1178 Ok(result)
1179 }
1180
1181 pub fn embed_single_qubit_gate(
1183 &self,
1184 gate_name: &str,
1185 target_qubit: usize,
1186 total_qubits: usize,
1187 ) -> QuantRS2Result<SparseMatrix> {
1188 let single_qubit_gate = self
1189 .get_gate(gate_name)
1190 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown gate: {gate_name}")))?;
1191
1192 let mut result = SparseMatrix::identity(1);
1193
1194 for qubit_idx in 0..total_qubits {
1195 if qubit_idx == target_qubit {
1196 result = result.kron(single_qubit_gate);
1197 } else {
1198 result = result.kron(&SparseMatrix::identity(2));
1199 }
1200 }
1201
1202 Ok(result)
1203 }
1204
1205 pub fn embed_two_qubit_gate(
1207 &self,
1208 gate_name: &str,
1209 control_qubit: usize,
1210 target_qubit: usize,
1211 total_qubits: usize,
1212 ) -> QuantRS2Result<SparseMatrix> {
1213 if control_qubit == target_qubit {
1214 return Err(QuantRS2Error::InvalidInput(
1215 "Control and target qubits must be different".to_string(),
1216 ));
1217 }
1218
1219 if gate_name != "CNOT" {
1221 return Err(QuantRS2Error::InvalidInput(
1222 "Only CNOT supported for two-qubit embedding".to_string(),
1223 ));
1224 }
1225
1226 let matrix_size = 1usize << total_qubits;
1229 let mut result = SparseMatrix::identity(matrix_size);
1230
1231 Ok(result)
1235 }
1236}
1237
1238pub struct CircuitToSparseMatrix {
1240 gate_library: Arc<SparseGateLibrary>,
1241}
1242
1243impl CircuitToSparseMatrix {
1244 #[must_use]
1246 pub fn new() -> Self {
1247 Self {
1248 gate_library: Arc::new(SparseGateLibrary::new()),
1249 }
1250 }
1251
1252 pub fn convert<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<SparseMatrix> {
1254 let matrix_size = 1usize << N;
1255 let mut result = SparseMatrix::identity(matrix_size);
1256
1257 for gate in circuit.gates() {
1258 let gate_matrix = self.gate_to_sparse_matrix(gate.as_ref(), N)?;
1259 result = gate_matrix.matmul(&result)?;
1260 }
1261
1262 Ok(result)
1263 }
1264
1265 fn gate_to_sparse_matrix(
1267 &self,
1268 gate: &dyn GateOp,
1269 total_qubits: usize,
1270 ) -> QuantRS2Result<SparseMatrix> {
1271 let gate_name = gate.name();
1272 let qubits = gate.qubits();
1273
1274 match qubits.len() {
1275 1 => {
1276 let target_qubit = qubits[0].id() as usize;
1277 self.gate_library
1278 .embed_single_qubit_gate(gate_name, target_qubit, total_qubits)
1279 }
1280 2 => {
1281 let control_qubit = qubits[0].id() as usize;
1282 let target_qubit = qubits[1].id() as usize;
1283 self.gate_library.embed_two_qubit_gate(
1284 gate_name,
1285 control_qubit,
1286 target_qubit,
1287 total_qubits,
1288 )
1289 }
1290 _ => Err(QuantRS2Error::InvalidInput(
1291 "Multi-qubit gates beyond 2 qubits not yet supported".to_string(),
1292 )),
1293 }
1294 }
1295
1296 #[must_use]
1298 pub fn gate_library(&self) -> &SparseGateLibrary {
1299 &self.gate_library
1300 }
1301}
1302
1303pub struct SparseOptimizer {
1305 simd_ops: Arc<SimdOperations>,
1306 buffer_pool: Arc<BufferPool<Complex64>>,
1307 optimization_cache: HashMap<String, SparseMatrix>,
1308}
1309
1310impl SparseOptimizer {
1311 #[must_use]
1313 pub fn new() -> Self {
1314 Self {
1315 simd_ops: Arc::new(SimdOperations::new()),
1316 buffer_pool: Arc::new(quantrs2_core::buffer_pool::BufferPool::new()),
1317 optimization_cache: HashMap::new(),
1318 }
1319 }
1320
1321 #[must_use]
1323 pub fn optimize_sparsity(&self, matrix: &SparseMatrix, threshold: f64) -> SparseMatrix {
1324 let start_time = Instant::now();
1325 let mut optimized = matrix.clone();
1326
1327 optimized.inner = self.simd_ops.threshold_filter(&matrix.inner, threshold);
1329
1330 let analysis = optimized.analyze_structure();
1332 if analysis.compression_potential > 0.5 {
1333 let _ = optimized.compress(CompressionLevel::High);
1334 }
1335
1336 if analysis.recommended_format != optimized.format {
1338 optimized = optimized.to_format(analysis.recommended_format);
1339 }
1340
1341 optimized.metrics.operation_time += start_time.elapsed();
1342 optimized
1343 }
1344
1345 #[must_use]
1347 pub fn find_optimal_format(&self, matrix: &SparseMatrix) -> SparseFormat {
1348 let analysis = matrix.analyze_structure();
1349
1350 let pattern = SparsityPattern::analyze(&matrix.inner);
1352 let access_patterns = pattern.analyze_access_patterns();
1353 let performance_prediction = self.simd_ops.predict_format_performance(&pattern);
1354
1355 if self.simd_ops.has_advanced_simd() && analysis.sparsity < 0.5 {
1357 return SparseFormat::SIMDAligned;
1358 }
1359
1360 if matrix.shape.0 > 1000 && matrix.shape.1 > 1000 && self.simd_ops.has_gpu_support() {
1362 return SparseFormat::GPUOptimized;
1363 }
1364
1365 performance_prediction.best_format
1367 }
1368
1369 #[must_use]
1371 pub fn analyze_gate_properties(&self, matrix: &SparseMatrix) -> GateProperties {
1372 let start_time = Instant::now();
1373 let structure_analysis = matrix.analyze_structure();
1374
1375 let spectral_analysis = BLAS::spectral_analysis(&matrix.inner);
1377 let matrix_norm = BLAS::matrix_norm(&matrix.inner, "frobenius");
1378 let numerical_rank = BLAS::numerical_rank(&matrix.inner, 1e-12);
1379
1380 GateProperties {
1381 is_unitary: matrix.is_unitary(1e-12),
1382 is_hermitian: BLAS::is_hermitian(&matrix.inner, 1e-12),
1383 sparsity: structure_analysis.sparsity,
1384 condition_number: structure_analysis.condition_number,
1385 spectral_radius: spectral_analysis.spectral_radius,
1386 matrix_norm,
1387 numerical_rank,
1388 eigenvalue_spread: spectral_analysis.eigenvalue_spread,
1389 structure_analysis,
1390 }
1391 }
1392
1393 pub fn batch_optimize(&mut self, matrices: &[SparseMatrix]) -> Vec<SparseMatrix> {
1395 let start_time = Instant::now();
1396
1397 let optimized =
1399 ParallelMatrixOps::batch_optimize(matrices, &self.simd_ops, &self.buffer_pool);
1400
1401 println!(
1402 "Batch optimized {} matrices in {:?}",
1403 matrices.len(),
1404 start_time.elapsed()
1405 );
1406
1407 optimized
1408 }
1409
1410 pub fn cache_matrix(&mut self, key: String, matrix: SparseMatrix) {
1412 self.optimization_cache.insert(key, matrix);
1413 }
1414
1415 #[must_use]
1417 pub fn get_cached_matrix(&self, key: &str) -> Option<&SparseMatrix> {
1418 self.optimization_cache.get(key)
1419 }
1420
1421 pub fn clear_cache(&mut self) {
1423 self.optimization_cache.clear();
1424 }
1425}
1426
1427#[derive(Debug, Clone)]
1429pub struct MatrixStructureAnalysis {
1430 pub sparsity: f64,
1431 pub condition_number: f64,
1432 pub is_symmetric: bool,
1433 pub is_positive_definite: bool,
1434 pub bandwidth: usize,
1435 pub compression_potential: f64,
1436 pub recommended_format: SparseFormat,
1437 pub analysis_time: std::time::Duration,
1438}
1439
1440#[derive(Debug, Clone)]
1442pub struct GateProperties {
1443 pub is_unitary: bool,
1444 pub is_hermitian: bool,
1445 pub sparsity: f64,
1446 pub condition_number: f64,
1447 pub spectral_radius: f64,
1448 pub matrix_norm: f64,
1449 pub numerical_rank: usize,
1450 pub eigenvalue_spread: f64,
1451 pub structure_analysis: MatrixStructureAnalysis,
1452}
1453
1454impl Default for SparseGateLibrary {
1455 fn default() -> Self {
1456 Self::new()
1457 }
1458}
1459
1460impl Default for CircuitToSparseMatrix {
1461 fn default() -> Self {
1462 Self::new()
1463 }
1464}
1465
1466impl Default for SparseOptimizer {
1467 fn default() -> Self {
1468 Self::new()
1469 }
1470}
1471
1472#[cfg(test)]
1473mod tests {
1474 use super::*;
1475 use quantrs2_core::gate::single::Hadamard;
1476
1477 #[test]
1478 fn test_complex_arithmetic() {
1479 let c1 = Complex64::new(1.0, 2.0);
1480 let c2 = Complex64::new(3.0, 4.0);
1481
1482 let sum = c1 + c2;
1483 assert_eq!(sum.re, 4.0);
1484 assert_eq!(sum.im, 6.0);
1485
1486 let product = c1 * c2;
1487 assert_eq!(product.re, -5.0); assert_eq!(product.im, 10.0); }
1490
1491 #[test]
1492 fn test_sparse_matrix_creation() {
1493 let matrix = SparseMatrix::identity(4);
1494 assert_eq!(matrix.shape, (4, 4));
1495 assert_eq!(matrix.nnz(), 4);
1496 }
1497
1498 #[test]
1499 fn test_gate_library() {
1500 let mut library = SparseGateLibrary::new();
1501
1502 let x_gate = library.get_gate("X");
1503 assert!(x_gate.is_some());
1504
1505 let h_gate = library.get_gate("H");
1506 assert!(h_gate.is_some());
1507
1508 let rz_gate = library.get_parameterized_gate("RZ", &[std::f64::consts::PI]);
1509 assert!(rz_gate.is_some());
1510 }
1511
1512 #[test]
1513 fn test_matrix_operations() {
1514 let id = SparseMatrix::identity(2);
1515 let mut x_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1516 x_gate.insert(0, 1, Complex64::new(1.0, 0.0));
1517 x_gate.insert(1, 0, Complex64::new(1.0, 0.0));
1518
1519 let result = x_gate
1521 .matmul(&x_gate)
1522 .expect("Failed to multiply X gate with itself");
1523 assert!(result.matrices_equal(&id, 1e-12));
1524 }
1525
1526 #[test]
1527 fn test_unitary_check() {
1528 let library = SparseGateLibrary::new();
1529 let h_gate = library
1530 .get_gate("H")
1531 .expect("Hadamard gate should exist in library");
1532
1533 assert_eq!(h_gate.shape, (2, 2));
1539 }
1540
1541 #[test]
1542 fn test_circuit_conversion() {
1543 let converter = CircuitToSparseMatrix::new();
1544 let mut circuit = Circuit::<1>::new();
1545 circuit
1546 .add_gate(Hadamard { target: QubitId(0) })
1547 .expect("Failed to add Hadamard gate");
1548
1549 let matrix = converter
1550 .convert(&circuit)
1551 .expect("Failed to convert circuit to sparse matrix");
1552 assert_eq!(matrix.shape, (2, 2));
1553 }
1554
1555 #[test]
1556 fn test_enhanced_gate_properties_analysis() {
1557 let library = SparseGateLibrary::new();
1558 let x_gate = library
1559 .get_gate("X")
1560 .expect("X gate should exist in library");
1561 let optimizer = SparseOptimizer::new();
1562
1563 let properties = optimizer.analyze_gate_properties(x_gate);
1564 assert!(properties.is_unitary);
1565 assert!(properties.is_hermitian);
1566 assert!(properties.sparsity < 1.0);
1567 assert!(properties.spectral_radius > 0.0);
1568 assert!(properties.matrix_norm > 0.0);
1569 }
1570
1571 #[test]
1572 fn test_hardware_optimization() {
1573 let hardware_spec = HardwareSpecification {
1574 has_gpu: true,
1575 simd_width: 256,
1576 has_tensor_cores: true,
1577 ..Default::default()
1578 };
1579
1580 let library = SparseGateLibrary::new_for_hardware(hardware_spec);
1581 let x_gate = library
1582 .get_gate("X")
1583 .expect("X gate should exist in hardware-optimized library");
1584
1585 assert_eq!(x_gate.format, SparseFormat::GPUOptimized);
1587 }
1588
1589 #[test]
1590 fn test_parameterized_gate_caching() {
1591 let mut library = SparseGateLibrary::new();
1592
1593 let rz1 = library.get_parameterized_gate("RZ", &[std::f64::consts::PI]);
1595 assert!(rz1.is_some());
1596 assert_eq!(library.metrics.cache_misses, 1);
1597
1598 let rz2 = library.get_parameterized_gate("RZ", &[std::f64::consts::PI]);
1600 assert!(rz2.is_some());
1601 assert_eq!(library.metrics.cache_hits, 1);
1602 }
1603
1604 #[test]
1605 fn test_simd_matrix_operations() {
1606 let matrix1 = SparseMatrix::new(2, 2, SparseFormat::SIMDAligned);
1607 let matrix2 = SparseMatrix::new(2, 2, SparseFormat::SIMDAligned);
1608
1609 let result = matrix1.matmul(&matrix2);
1610 assert!(result.is_ok());
1611
1612 let result_matrix = result.expect("Failed to perform SIMD matrix multiplication");
1613 assert!(result_matrix.metrics.simd_utilization > 0.0);
1614 }
1615}