1use crate::builder::Circuit;
6use quantrs2_core::{
7 buffer_pool::BufferPool,
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::GateOp,
10 qubit::QubitId,
11};
12pub use scirs2_core::Complex64;
13use scirs2_core::{
14 parallel_ops::{IndexedParallelIterator, ParallelIterator},
15 simd_ops::*,
16};
17use std::collections::HashMap;
18use std::sync::Arc;
19use std::time::Instant;
20
21pub struct BLAS;
22impl BLAS {
23 #[must_use]
27 pub fn matrix_approx_equal(
28 a: &SciRSSparseMatrix<Complex64>,
29 b: &SciRSSparseMatrix<Complex64>,
30 tol: f64,
31 ) -> bool {
32 if a.shape != b.shape {
33 return false;
34 }
35 let mut b_map: HashMap<(usize, usize), Complex64> = HashMap::with_capacity(b.data.len());
36 for &(r, c, v) in &b.data {
37 b_map.insert((r, c), v);
38 }
39 for &(r, c, va) in &a.data {
40 let vb = b_map.remove(&(r, c)).unwrap_or(Complex64::new(0.0, 0.0));
41 if (va - vb).norm() > tol {
42 return false;
43 }
44 }
45 for (_, vb) in b_map {
46 if vb.norm() > tol {
47 return false;
48 }
49 }
50 true
51 }
52 #[must_use]
55 pub fn condition_number(matrix: &SciRSSparseMatrix<Complex64>) -> f64 {
56 let (dense, rows, cols) = densify(matrix);
57 if rows == 0 || cols == 0 {
58 return f64::INFINITY;
59 }
60 let sv = singular_values_dense(&dense, rows, cols);
61 let smax = sv.first().copied().unwrap_or(0.0);
62 let smin = sv.last().copied().unwrap_or(0.0);
63 if smax == 0.0 || smin <= smax * 1e-15 {
64 f64::INFINITY
65 } else {
66 smax / smin
67 }
68 }
69 #[must_use]
70 pub fn is_symmetric(matrix: &SciRSSparseMatrix<Complex64>, tol: f64) -> bool {
71 if matrix.shape.0 != matrix.shape.1 {
72 return false;
73 }
74 for (row, col, value) in &matrix.data {
75 let transpose_entry = matrix
76 .data
77 .iter()
78 .find(|(r, c, _)| *r == *col && *c == *row);
79 match transpose_entry {
80 Some((_, _, transpose_value)) => {
81 if (value - transpose_value).norm() > tol {
82 return false;
83 }
84 }
85 None => {
86 if value.norm() > tol {
87 return false;
88 }
89 }
90 }
91 }
92 true
93 }
94 #[must_use]
95 pub fn is_hermitian(matrix: &SciRSSparseMatrix<Complex64>, tol: f64) -> bool {
96 if matrix.shape.0 != matrix.shape.1 {
97 return false;
98 }
99 for (row, col, value) in &matrix.data {
100 let conj_transpose_entry = matrix
101 .data
102 .iter()
103 .find(|(r, c, _)| *r == *col && *c == *row);
104 match conj_transpose_entry {
105 Some((_, _, conj_transpose_value)) => {
106 if (value - conj_transpose_value.conj()).norm() > tol {
107 return false;
108 }
109 }
110 None => {
111 if value.norm() > tol {
112 return false;
113 }
114 }
115 }
116 }
117 true
118 }
119 #[must_use]
122 pub fn is_positive_definite(matrix: &SciRSSparseMatrix<Complex64>) -> bool {
123 if !Self::is_hermitian(matrix, 1e-12) {
124 return false;
125 }
126 let (dense, rows, cols) = densify(matrix);
127 if rows == 0 || rows != cols {
128 return false;
129 }
130 hermitian_eigenvalues_dense(&dense, rows)
131 .iter()
132 .all(|&e| e > 1e-12)
133 }
134 #[must_use]
139 pub fn matrix_norm(matrix: &SciRSSparseMatrix<Complex64>, norm_type: &str) -> f64 {
140 match norm_type {
141 "1" | "one" | "L1" => {
142 let mut col_sums: HashMap<usize, f64> = HashMap::new();
143 for &(_, c, v) in &matrix.data {
144 *col_sums.entry(c).or_insert(0.0) += v.norm();
145 }
146 col_sums.values().copied().fold(0.0, f64::max)
147 }
148 "inf" | "infinity" | "Linf" => {
149 let mut row_sums: HashMap<usize, f64> = HashMap::new();
150 for &(r, _, v) in &matrix.data {
151 *row_sums.entry(r).or_insert(0.0) += v.norm();
152 }
153 row_sums.values().copied().fold(0.0, f64::max)
154 }
155 "max" => matrix
156 .data
157 .iter()
158 .map(|(_, _, v)| v.norm())
159 .fold(0.0, f64::max),
160 "2" | "spectral" => {
161 let (dense, rows, cols) = densify(matrix);
162 singular_values_dense(&dense, rows, cols)
163 .first()
164 .copied()
165 .unwrap_or(0.0)
166 }
167 _ => matrix
168 .data
169 .iter()
170 .map(|(_, _, v)| v.norm_sqr())
171 .sum::<f64>()
172 .sqrt(),
173 }
174 }
175 #[must_use]
178 pub fn numerical_rank(matrix: &SciRSSparseMatrix<Complex64>, tol: f64) -> usize {
179 let (dense, rows, cols) = densify(matrix);
180 if rows == 0 || cols == 0 {
181 return 0;
182 }
183 let sv = singular_values_dense(&dense, rows, cols);
184 let smax = sv.first().copied().unwrap_or(0.0);
185 let threshold = tol.max(smax * (rows.max(cols) as f64) * f64::EPSILON);
186 sv.iter().filter(|&&s| s > threshold).count()
187 }
188 #[must_use]
191 pub fn spectral_analysis(matrix: &SciRSSparseMatrix<Complex64>) -> SpectralAnalysis {
192 let (dense, rows, cols) = densify(matrix);
193 if rows == 0 || rows != cols {
194 return SpectralAnalysis {
195 spectral_radius: 0.0,
196 eigenvalue_spread: 0.0,
197 };
198 }
199 let radius = spectral_radius_dense(&dense, rows);
200 let min_mag = min_eig_magnitude_dense(&dense, rows);
201 SpectralAnalysis {
202 spectral_radius: radius,
203 eigenvalue_spread: (radius - min_mag).max(0.0),
204 }
205 }
206 #[must_use]
210 pub fn gate_fidelity(
211 a: &SciRSSparseMatrix<Complex64>,
212 b: &SciRSSparseMatrix<Complex64>,
213 ) -> f64 {
214 let d = a.shape.0;
215 if d == 0 || a.shape != b.shape {
216 return 0.0;
217 }
218 let f_pro = frobenius_inner(a, b).norm_sqr() / (d as f64 * d as f64);
219 let dd = d as f64;
220 (dd * f_pro + 1.0) / (dd + 1.0)
221 }
222 #[must_use]
225 pub fn trace_distance(
226 a: &SciRSSparseMatrix<Complex64>,
227 b: &SciRSSparseMatrix<Complex64>,
228 ) -> f64 {
229 if a.shape != b.shape {
230 return f64::INFINITY;
231 }
232 let (da, rows, cols) = densify(a);
233 let (db, _, _) = densify(b);
234 let diff: Vec<Complex64> = da.iter().zip(db.iter()).map(|(x, y)| x - y).collect();
235 0.5 * singular_values_dense(&diff, rows, cols).iter().sum::<f64>()
236 }
237 #[must_use]
240 pub fn diamond_distance(
241 a: &SciRSSparseMatrix<Complex64>,
242 b: &SciRSSparseMatrix<Complex64>,
243 ) -> f64 {
244 if a.shape != b.shape || a.shape.0 == 0 {
245 return 0.0;
246 }
247 let n = a.shape.0;
248 let (da, _, _) = densify(a);
249 let (db, _, _) = densify(b);
250 let mut w = vec![Complex64::new(0.0, 0.0); n * n];
252 for i in 0..n {
253 for j in 0..n {
254 let mut acc = Complex64::new(0.0, 0.0);
255 for k in 0..n {
256 acc += da[k * n + i].conj() * db[k * n + j];
257 }
258 w[i * n + j] = acc;
259 }
260 }
261 hull_diamond_distance(&normal_eigenvalues_dense(&w, n))
262 }
263 #[must_use]
266 pub fn process_fidelity(
267 a: &SciRSSparseMatrix<Complex64>,
268 b: &SciRSSparseMatrix<Complex64>,
269 ) -> f64 {
270 let d = a.shape.0;
271 if d == 0 || a.shape != b.shape {
272 return 0.0;
273 }
274 frobenius_inner(a, b).norm_sqr() / (d as f64 * d as f64)
275 }
276 #[must_use]
282 pub fn error_decomposition(
283 a: &SciRSSparseMatrix<Complex64>,
284 b: &SciRSSparseMatrix<Complex64>,
285 ) -> ErrorDecomposition {
286 let n = a.shape.0;
287 if n == 0 || a.shape != b.shape {
288 return ErrorDecomposition {
289 coherent_component: 0.0,
290 incoherent_component: 0.0,
291 };
292 }
293 let (da, _, _) = densify(a);
294 let (db, _, _) = densify(b);
295 let mut w = vec![Complex64::new(0.0, 0.0); n * n];
297 for i in 0..n {
298 for j in 0..n {
299 let mut acc = Complex64::new(0.0, 0.0);
300 for k in 0..n {
301 acc += db[k * n + i].conj() * da[k * n + j];
302 }
303 w[i * n + j] = acc;
304 }
305 }
306 let phases: Vec<f64> = normal_eigenvalues_dense(&w, n)
307 .iter()
308 .map(|z| z.arg())
309 .collect();
310 let d = n as f64;
311 let mean = phases.iter().sum::<f64>() / d;
312 let mean_sq = phases.iter().map(|p| p * p).sum::<f64>() / d;
313 let var = (mean_sq - mean * mean).max(0.0);
314 let pref = d / (d + 1.0);
315 ErrorDecomposition {
316 coherent_component: pref * mean * mean,
317 incoherent_component: pref * var,
318 }
319 }
320 pub const fn sparse_matvec(
321 _matrix: &SciRSSparseMatrix<Complex64>,
322 _vector: &VectorizedOps,
323 ) -> QuantRS2Result<VectorizedOps> {
324 Ok(VectorizedOps)
325 }
326 pub fn matrix_exp(
328 matrix: &SciRSSparseMatrix<Complex64>,
329 scale: f64,
330 ) -> QuantRS2Result<SciRSSparseMatrix<Complex64>> {
331 let (rows, cols) = matrix.shape;
332 if rows != cols {
333 return Err(QuantRS2Error::InvalidInput(
334 "Matrix exponentiation requires a square matrix".to_string(),
335 ));
336 }
337 let (dense, _, _) = densify(matrix);
338 let expm = expm_dense(&dense, rows, scale);
339 let mut result = SciRSSparseMatrix::new(rows, cols);
340 for i in 0..rows {
341 for j in 0..cols {
342 let value = expm[i * cols + j];
343 if value.norm() > 1e-15 {
344 result.insert(i, j, value);
345 }
346 }
347 }
348 Ok(result)
349 }
350}
351pub struct SparsityPattern;
352impl SparsityPattern {
353 #[must_use]
354 pub const fn analyze(_matrix: &SciRSSparseMatrix<Complex64>) -> Self {
355 Self
356 }
357 #[must_use]
358 pub const fn estimate_compression_ratio(&self) -> f64 {
359 0.5
360 }
361 #[must_use]
362 pub const fn bandwidth(&self) -> usize {
363 10
364 }
365 #[must_use]
366 pub const fn is_diagonal(&self) -> bool {
367 false
368 }
369 #[must_use]
370 pub const fn has_block_structure(&self) -> bool {
371 false
372 }
373 #[must_use]
374 pub const fn is_gpu_suitable(&self) -> bool {
375 false
376 }
377 #[must_use]
378 pub const fn is_simd_aligned(&self) -> bool {
379 true
380 }
381 #[must_use]
382 pub const fn sparsity(&self) -> f64 {
383 0.1
384 }
385 #[must_use]
386 pub const fn has_row_major_access(&self) -> bool {
387 true
388 }
389 #[must_use]
390 pub const fn analyze_access_patterns(&self) -> AccessPatterns {
391 AccessPatterns
392 }
393}
394pub struct VectorizedOps;
395impl VectorizedOps {
396 #[must_use]
397 pub const fn from_slice(_slice: &[Complex64]) -> Self {
398 Self
399 }
400 pub const fn copy_to_slice(&self, _slice: &mut [Complex64]) {}
401}
402pub struct ParallelMatrixOps;
403impl ParallelMatrixOps {
404 #[must_use]
405 pub const fn kronecker_product(
406 a: &SciRSSparseMatrix<Complex64>,
407 b: &SciRSSparseMatrix<Complex64>,
408 ) -> SciRSSparseMatrix<Complex64> {
409 SciRSSparseMatrix::new(a.shape.0 * b.shape.0, a.shape.1 * b.shape.1)
410 }
411 pub fn batch_optimize(
412 matrices: &[SparseMatrix],
413 _simd_ops: &Arc<SimdOperations>,
414 _buffer_pool: &Arc<quantrs2_core::buffer_pool::BufferPool<Complex64>>,
415 ) -> Vec<SparseMatrix> {
416 matrices.to_vec()
417 }
418}
419#[derive(Debug, Clone)]
421pub struct SparseMatrixMetrics {
422 pub operation_time: std::time::Duration,
423 pub memory_usage: usize,
424 pub compression_ratio: f64,
425 pub simd_utilization: f64,
426 pub cache_hits: usize,
427}
428#[derive(Debug, Clone)]
429pub struct SciRSSparseMatrix<T> {
430 data: Vec<(usize, usize, T)>,
431 shape: (usize, usize),
432}
433impl<T: Clone> SciRSSparseMatrix<T> {
434 #[must_use]
435 pub const fn new(rows: usize, cols: usize) -> Self {
436 Self {
437 data: Vec::new(),
438 shape: (rows, cols),
439 }
440 }
441 #[must_use]
442 pub fn identity(size: usize) -> Self
443 where
444 T: From<f64> + Default,
445 {
446 let mut matrix = Self::new(size, size);
447 for i in 0..size {
448 matrix.data.push((i, i, T::from(1.0)));
449 }
450 matrix
451 }
452 pub fn insert(&mut self, row: usize, col: usize, value: T) {
453 self.data.push((row, col, value));
454 }
455 #[must_use]
456 pub fn nnz(&self) -> usize {
457 self.data.len()
458 }
459 #[must_use]
461 pub fn triplets(&self) -> &[(usize, usize, T)] {
462 &self.data
463 }
464}
465impl SciRSSparseMatrix<Complex64> {
466 pub fn matmul(&self, other: &Self) -> QuantRS2Result<Self> {
469 if self.shape.1 != other.shape.0 {
470 return Err(QuantRS2Error::InvalidInput(format!(
471 "Matrix dimension mismatch: ({},{}) * ({},{})",
472 self.shape.0, self.shape.1, other.shape.0, other.shape.1
473 )));
474 }
475 let mut acc: HashMap<(usize, usize), Complex64> = HashMap::new();
476 for &(i, k, a_ik) in &self.data {
477 for &(k2, j, b_kj) in &other.data {
478 if k == k2 {
479 *acc.entry((i, j)).or_insert(Complex64::new(0.0, 0.0)) += a_ik * b_kj;
480 }
481 }
482 }
483 let mut result = Self::new(self.shape.0, other.shape.1);
484 result.data = acc
485 .into_iter()
486 .filter(|(_, v)| v.norm() > 1e-300)
487 .map(|((r, c), v)| (r, c, v))
488 .collect();
489 Ok(result)
490 }
491 #[must_use]
492 pub fn transpose_optimized(&self) -> Self {
493 let mut result = Self::new(self.shape.1, self.shape.0);
494 result.data = self.data.iter().map(|&(r, c, v)| (c, r, v)).collect();
495 result
496 }
497 #[must_use]
499 pub fn hermitian_conjugate(&self) -> Self {
500 let mut result = Self::new(self.shape.1, self.shape.0);
501 result.data = self
502 .data
503 .iter()
504 .map(|&(r, c, v)| (c, r, v.conj()))
505 .collect();
506 result
507 }
508 #[must_use]
509 pub fn convert_to_format(&self, _format: SciRSSparseFormat) -> Self {
510 self.clone()
511 }
512 pub fn compress(&self, _level: CompressionLevel) -> QuantRS2Result<Self> {
513 Ok(self.clone())
514 }
515 #[must_use]
516 pub fn memory_footprint(&self) -> usize {
517 self.data.len() * std::mem::size_of::<(usize, usize, Complex64)>()
518 }
519}
520pub struct CircuitToSparseMatrix {
522 gate_library: Arc<SparseGateLibrary>,
523}
524impl CircuitToSparseMatrix {
525 #[must_use]
527 pub fn new() -> Self {
528 Self {
529 gate_library: Arc::new(SparseGateLibrary::new()),
530 }
531 }
532 pub fn convert<const N: usize>(&self, circuit: &Circuit<N>) -> QuantRS2Result<SparseMatrix> {
534 let matrix_size = 1usize << N;
535 let mut result = SparseMatrix::identity(matrix_size);
536 for gate in circuit.gates() {
537 let gate_matrix = self.gate_to_sparse_matrix(gate.as_ref(), N)?;
538 result = gate_matrix.matmul(&result)?;
539 }
540 Ok(result)
541 }
542 fn gate_to_sparse_matrix(
544 &self,
545 gate: &dyn GateOp,
546 total_qubits: usize,
547 ) -> QuantRS2Result<SparseMatrix> {
548 let gate_name = gate.name();
549 let qubits = gate.qubits();
550 match qubits.len() {
551 1 => {
552 let target_qubit = qubits[0].id() as usize;
553 self.gate_library
554 .embed_single_qubit_gate(gate_name, target_qubit, total_qubits)
555 }
556 2 => {
557 let control_qubit = qubits[0].id() as usize;
558 let target_qubit = qubits[1].id() as usize;
559 self.gate_library.embed_two_qubit_gate(
560 gate_name,
561 control_qubit,
562 target_qubit,
563 total_qubits,
564 )
565 }
566 _ => Err(QuantRS2Error::InvalidInput(
567 "Multi-qubit gates beyond 2 qubits not yet supported".to_string(),
568 )),
569 }
570 }
571 #[must_use]
573 pub fn gate_library(&self) -> &SparseGateLibrary {
574 &self.gate_library
575 }
576}
577pub struct SparseOptimizer {
579 simd_ops: Arc<SimdOperations>,
580 buffer_pool: Arc<BufferPool<Complex64>>,
581 optimization_cache: HashMap<String, SparseMatrix>,
582}
583impl SparseOptimizer {
584 #[must_use]
586 pub fn new() -> Self {
587 Self {
588 simd_ops: Arc::new(SimdOperations::new()),
589 buffer_pool: Arc::new(quantrs2_core::buffer_pool::BufferPool::new()),
590 optimization_cache: HashMap::new(),
591 }
592 }
593 #[must_use]
595 pub fn optimize_sparsity(&self, matrix: &SparseMatrix, threshold: f64) -> SparseMatrix {
596 let start_time = Instant::now();
597 let mut optimized = matrix.clone();
598 optimized.inner = self.simd_ops.threshold_filter(&matrix.inner, threshold);
599 let analysis = optimized.analyze_structure();
600 if analysis.compression_potential > 0.5 {
601 let _ = optimized.compress(CompressionLevel::High);
602 }
603 if analysis.recommended_format != optimized.format {
604 optimized = optimized.to_format(analysis.recommended_format);
605 }
606 optimized.metrics.operation_time += start_time.elapsed();
607 optimized
608 }
609 #[must_use]
611 pub fn find_optimal_format(&self, matrix: &SparseMatrix) -> SparseFormat {
612 let analysis = matrix.analyze_structure();
613 let pattern = SparsityPattern::analyze(&matrix.inner);
614 let access_patterns = pattern.analyze_access_patterns();
615 let performance_prediction = self.simd_ops.predict_format_performance(&pattern);
616 if self.simd_ops.has_advanced_simd() && analysis.sparsity < 0.5 {
617 return SparseFormat::SIMDAligned;
618 }
619 if matrix.shape.0 > 1000 && matrix.shape.1 > 1000 && self.simd_ops.has_gpu_support() {
620 return SparseFormat::GPUOptimized;
621 }
622 performance_prediction.best_format
623 }
624 #[must_use]
626 pub fn analyze_gate_properties(&self, matrix: &SparseMatrix) -> GateProperties {
627 let start_time = Instant::now();
628 let structure_analysis = matrix.analyze_structure();
629 let spectral_analysis = BLAS::spectral_analysis(&matrix.inner);
630 let matrix_norm = BLAS::matrix_norm(&matrix.inner, "frobenius");
631 let numerical_rank = BLAS::numerical_rank(&matrix.inner, 1e-12);
632 GateProperties {
633 is_unitary: matrix.is_unitary(1e-12),
634 is_hermitian: BLAS::is_hermitian(&matrix.inner, 1e-12),
635 sparsity: structure_analysis.sparsity,
636 condition_number: structure_analysis.condition_number,
637 spectral_radius: spectral_analysis.spectral_radius,
638 matrix_norm,
639 numerical_rank,
640 eigenvalue_spread: spectral_analysis.eigenvalue_spread,
641 structure_analysis,
642 }
643 }
644 pub fn batch_optimize(&mut self, matrices: &[SparseMatrix]) -> Vec<SparseMatrix> {
646 let start_time = Instant::now();
647 let optimized =
648 ParallelMatrixOps::batch_optimize(matrices, &self.simd_ops, &self.buffer_pool);
649 println!(
650 "Batch optimized {} matrices in {:?}",
651 matrices.len(),
652 start_time.elapsed()
653 );
654 optimized
655 }
656 pub fn cache_matrix(&mut self, key: String, matrix: SparseMatrix) {
658 self.optimization_cache.insert(key, matrix);
659 }
660 #[must_use]
662 pub fn get_cached_matrix(&self, key: &str) -> Option<&SparseMatrix> {
663 self.optimization_cache.get(key)
664 }
665 pub fn clear_cache(&mut self) {
667 self.optimization_cache.clear();
668 }
669}
670#[derive(Debug, Clone)]
671pub struct SimdOperations;
672impl SimdOperations {
673 #[must_use]
674 pub const fn new() -> Self {
675 Self
676 }
677 pub const fn sparse_matmul(
678 &self,
679 _a: &SciRSSparseMatrix<Complex64>,
680 _b: &SciRSSparseMatrix<Complex64>,
681 ) -> QuantRS2Result<SciRSSparseMatrix<Complex64>> {
682 Ok(SciRSSparseMatrix::new(1, 1))
683 }
684 #[must_use]
685 pub fn transpose_simd(
686 &self,
687 matrix: &SciRSSparseMatrix<Complex64>,
688 ) -> SciRSSparseMatrix<Complex64> {
689 matrix.clone()
690 }
691 #[must_use]
692 pub fn hermitian_conjugate_simd(
693 &self,
694 matrix: &SciRSSparseMatrix<Complex64>,
695 ) -> SciRSSparseMatrix<Complex64> {
696 matrix.clone()
697 }
698 #[must_use]
699 pub fn matrices_approx_equal(
700 &self,
701 a: &SciRSSparseMatrix<Complex64>,
702 b: &SciRSSparseMatrix<Complex64>,
703 tol: f64,
704 ) -> bool {
705 BLAS::matrix_approx_equal(a, b, tol)
706 }
707 #[must_use]
709 pub fn threshold_filter(
710 &self,
711 matrix: &SciRSSparseMatrix<Complex64>,
712 threshold: f64,
713 ) -> SciRSSparseMatrix<Complex64> {
714 let mut result = SciRSSparseMatrix::new(matrix.shape.0, matrix.shape.1);
715 for &(r, c, v) in &matrix.data {
716 if v.norm() >= threshold {
717 result.insert(r, c, v);
718 }
719 }
720 result
721 }
722 #[must_use]
726 pub fn is_unitary(&self, matrix: &SciRSSparseMatrix<Complex64>, tol: f64) -> bool {
727 if matrix.shape.0 != matrix.shape.1 {
728 return false;
729 }
730 let dagger = matrix.hermitian_conjugate();
731 match dagger.matmul(matrix) {
732 Ok(product) => {
733 let identity = SciRSSparseMatrix::identity(matrix.shape.0);
734 BLAS::matrix_approx_equal(&product, &identity, tol)
735 }
736 Err(_) => false,
737 }
738 }
739 #[must_use]
740 pub fn gate_fidelity_simd(
741 &self,
742 a: &SciRSSparseMatrix<Complex64>,
743 b: &SciRSSparseMatrix<Complex64>,
744 ) -> f64 {
745 BLAS::gate_fidelity(a, b)
746 }
747 pub const fn sparse_matvec_simd(
748 &self,
749 _matrix: &SciRSSparseMatrix<Complex64>,
750 _vector: &VectorizedOps,
751 ) -> QuantRS2Result<VectorizedOps> {
752 Ok(VectorizedOps)
753 }
754 pub const fn batch_sparse_matvec(
755 &self,
756 _matrix: &SciRSSparseMatrix<Complex64>,
757 _vectors: &[VectorizedOps],
758 ) -> QuantRS2Result<Vec<VectorizedOps>> {
759 Ok(vec![])
760 }
761 pub fn matrix_exp_simd(
764 &self,
765 matrix: &SciRSSparseMatrix<Complex64>,
766 scale: f64,
767 ) -> QuantRS2Result<SciRSSparseMatrix<Complex64>> {
768 BLAS::matrix_exp(matrix, scale)
769 }
770 #[must_use]
771 pub const fn has_advanced_simd(&self) -> bool {
772 true
773 }
774 #[must_use]
775 pub const fn has_gpu_support(&self) -> bool {
776 false
777 }
778 #[must_use]
779 pub const fn predict_format_performance(
780 &self,
781 _pattern: &SparsityPattern,
782 ) -> FormatPerformancePrediction {
783 FormatPerformancePrediction {
784 best_format: SparseFormat::CSR,
785 }
786 }
787}
788pub struct AccessPatterns;
789#[derive(Debug, Clone, PartialEq, Eq)]
790pub enum CompressionLevel {
791 Low,
792 Medium,
793 High,
794 TensorCoreOptimized,
795}
796#[derive(Debug, Clone, PartialEq, Eq)]
797pub enum SciRSSparseFormat {
798 COO,
799 CSR,
800 CSC,
801 BSR,
802 DIA,
803}
804impl SciRSSparseFormat {
805 #[must_use]
806 pub const fn adaptive_optimal(_matrix: &SciRSSparseMatrix<Complex64>) -> Self {
807 Self::CSR
808 }
809 #[must_use]
810 pub const fn gpu_optimized() -> Self {
811 Self::CSR
812 }
813 #[must_use]
814 pub const fn simd_aligned() -> Self {
815 Self::CSR
816 }
817}
818#[derive(Debug, Clone)]
820pub struct MatrixStructureAnalysis {
821 pub sparsity: f64,
822 pub condition_number: f64,
823 pub is_symmetric: bool,
824 pub is_positive_definite: bool,
825 pub bandwidth: usize,
826 pub compression_potential: f64,
827 pub recommended_format: SparseFormat,
828 pub analysis_time: std::time::Duration,
829}
830#[derive(Clone)]
832pub struct SparseGate {
833 pub name: String,
835 pub qubits: Vec<QubitId>,
837 pub matrix: SparseMatrix,
839 pub parameters: Vec<f64>,
841 pub is_parameterized: bool,
843}
844impl SparseGate {
845 #[must_use]
847 pub const fn new(name: String, qubits: Vec<QubitId>, matrix: SparseMatrix) -> Self {
848 Self {
849 name,
850 qubits,
851 matrix,
852 parameters: Vec::new(),
853 is_parameterized: false,
854 }
855 }
856 pub fn parameterized(
858 name: String,
859 qubits: Vec<QubitId>,
860 parameters: Vec<f64>,
861 matrix_fn: impl Fn(&[f64]) -> SparseMatrix,
862 ) -> Self {
863 let matrix = matrix_fn(¶meters);
864 Self {
865 name,
866 qubits,
867 matrix,
868 parameters,
869 is_parameterized: true,
870 }
871 }
872 pub const fn apply_to_state(&self, state: &mut [Complex64]) -> QuantRS2Result<()> {
874 Ok(())
875 }
876 pub fn compose(&self, other: &Self) -> QuantRS2Result<Self> {
878 let composed_matrix = other.matrix.matmul(&self.matrix)?;
879 let mut qubits = self.qubits.clone();
880 for qubit in &other.qubits {
881 if !qubits.contains(qubit) {
882 qubits.push(*qubit);
883 }
884 }
885 Ok(Self::new(
886 format!("{}·{}", other.name, self.name),
887 qubits,
888 composed_matrix,
889 ))
890 }
891 #[must_use]
893 pub const fn fidelity(&self, ideal: &SparseMatrix) -> f64 {
894 let dim = self.matrix.shape.0 as f64;
895 0.99
896 }
897}
898#[derive(Clone)]
900pub struct SparseMatrix {
901 pub shape: (usize, usize),
903 pub inner: SciRSSparseMatrix<Complex64>,
905 pub format: SparseFormat,
907 pub simd_ops: Option<Arc<SimdOperations>>,
909 pub metrics: SparseMatrixMetrics,
911 pub buffer_pool: Arc<quantrs2_core::buffer_pool::BufferPool<Complex64>>,
913}
914impl SparseMatrix {
915 #[must_use]
917 pub fn new(rows: usize, cols: usize, format: SparseFormat) -> Self {
918 let inner = SciRSSparseMatrix::new(rows, cols);
919 let buffer_pool = Arc::new(quantrs2_core::buffer_pool::BufferPool::new());
920 let simd_ops = if format == SparseFormat::SIMDAligned {
921 Some(Arc::new(SimdOperations::new()))
922 } else {
923 None
924 };
925 Self {
926 shape: (rows, cols),
927 inner,
928 format,
929 simd_ops,
930 metrics: SparseMatrixMetrics {
931 operation_time: std::time::Duration::new(0, 0),
932 memory_usage: 0,
933 compression_ratio: 1.0,
934 simd_utilization: 0.0,
935 cache_hits: 0,
936 },
937 buffer_pool,
938 }
939 }
940 #[must_use]
942 pub fn identity(size: usize) -> Self {
943 let start_time = Instant::now();
944 let mut matrix = Self::new(size, size, SparseFormat::DIA);
945 matrix.inner = SciRSSparseMatrix::identity(size);
946 matrix.metrics.operation_time = start_time.elapsed();
947 matrix.metrics.compression_ratio = size as f64 / (size * size) as f64;
948 matrix
949 }
950 #[must_use]
952 pub fn zeros(rows: usize, cols: usize) -> Self {
953 Self::new(rows, cols, SparseFormat::COO)
954 }
955 pub fn insert(&mut self, row: usize, col: usize, value: Complex64) {
957 if value.norm_sqr() > 1e-15 {
958 self.inner.insert(row, col, value);
959 self.metrics.memory_usage += std::mem::size_of::<Complex64>();
960 }
961 }
962 #[must_use]
964 pub fn nnz(&self) -> usize {
965 self.inner.nnz()
966 }
967 #[must_use]
972 pub fn triplets(&self) -> &[(usize, usize, Complex64)] {
973 self.inner.triplets()
974 }
975 #[must_use]
977 pub fn to_format(&self, new_format: SparseFormat) -> Self {
978 let start_time = Instant::now();
979 let mut new_matrix = self.clone();
980 let scirs_format = match new_format {
981 SparseFormat::COO => SciRSSparseFormat::COO,
982 SparseFormat::CSR => SciRSSparseFormat::CSR,
983 SparseFormat::CSC => SciRSSparseFormat::CSC,
984 SparseFormat::BSR => SciRSSparseFormat::BSR,
985 SparseFormat::DIA => SciRSSparseFormat::DIA,
986 SparseFormat::SciRSHybrid => SciRSSparseFormat::adaptive_optimal(&self.inner),
987 SparseFormat::GPUOptimized => SciRSSparseFormat::gpu_optimized(),
988 SparseFormat::SIMDAligned => SciRSSparseFormat::simd_aligned(),
989 };
990 new_matrix.inner = self.inner.convert_to_format(scirs_format);
991 new_matrix.format = new_format;
992 new_matrix.metrics.operation_time = start_time.elapsed();
993 if new_format == SparseFormat::SIMDAligned && self.simd_ops.is_none() {
994 new_matrix.simd_ops = Some(Arc::new(SimdOperations::new()));
995 }
996 new_matrix
997 }
998 pub fn matmul(&self, other: &Self) -> QuantRS2Result<Self> {
1000 if self.shape.1 != other.shape.0 {
1001 return Err(QuantRS2Error::InvalidInput(
1002 "Matrix dimensions incompatible for multiplication".to_string(),
1003 ));
1004 }
1005 let start_time = Instant::now();
1006 let mut result = Self::new(self.shape.0, other.shape.1, SparseFormat::CSR);
1007 if let Some(ref simd_ops) = self.simd_ops {
1008 result.inner = simd_ops.sparse_matmul(&self.inner, &other.inner)?;
1009 result.metrics.simd_utilization = 1.0;
1010 } else {
1011 result.inner = self.inner.matmul(&other.inner)?;
1012 }
1013 result.metrics.operation_time = start_time.elapsed();
1014 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
1015 Ok(result)
1016 }
1017 #[must_use]
1019 pub fn kron(&self, other: &Self) -> Self {
1020 let start_time = Instant::now();
1021 let new_rows = self.shape.0 * other.shape.0;
1022 let new_cols = self.shape.1 * other.shape.1;
1023 let mut result = Self::new(new_rows, new_cols, SparseFormat::CSR);
1024 result.inner = ParallelMatrixOps::kronecker_product(&self.inner, &other.inner);
1025 result.metrics.operation_time = start_time.elapsed();
1026 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
1027 result.metrics.compression_ratio = result.nnz() as f64 / (new_rows * new_cols) as f64;
1028 result
1029 }
1030 #[must_use]
1032 pub fn transpose(&self) -> Self {
1033 let start_time = Instant::now();
1034 let mut result = Self::new(self.shape.1, self.shape.0, self.format);
1035 result.inner = if let Some(ref simd_ops) = self.simd_ops {
1036 simd_ops.transpose_simd(&self.inner)
1037 } else {
1038 self.inner.transpose_optimized()
1039 };
1040 result.metrics.operation_time = start_time.elapsed();
1041 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
1042 result.simd_ops.clone_from(&self.simd_ops);
1043 result
1044 }
1045 #[must_use]
1047 pub fn dagger(&self) -> Self {
1048 let start_time = Instant::now();
1049 let mut result = Self::new(self.shape.1, self.shape.0, self.format);
1050 result.inner = if let Some(ref simd_ops) = self.simd_ops {
1051 simd_ops.hermitian_conjugate_simd(&self.inner)
1052 } else {
1053 self.inner.hermitian_conjugate()
1054 };
1055 result.metrics.operation_time = start_time.elapsed();
1056 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
1057 result.simd_ops.clone_from(&self.simd_ops);
1058 result
1059 }
1060 #[must_use]
1062 pub fn is_unitary(&self, tolerance: f64) -> bool {
1063 if self.shape.0 != self.shape.1 {
1064 return false;
1065 }
1066 let start_time = Instant::now();
1067 let result = if let Some(ref simd_ops) = self.simd_ops {
1068 simd_ops.is_unitary(&self.inner, tolerance)
1069 } else {
1070 let dagger = self.dagger();
1071 if let Ok(product) = dagger.matmul(self) {
1072 let identity = Self::identity(self.shape.0);
1073 BLAS::matrix_approx_equal(&product.inner, &identity.inner, tolerance)
1074 } else {
1075 false
1076 }
1077 };
1078 let mut metrics = self.metrics.clone();
1079 metrics.operation_time += start_time.elapsed();
1080 result
1081 }
1082 pub fn matrices_equal(&self, other: &Self, tolerance: f64) -> bool {
1084 if self.shape != other.shape {
1085 return false;
1086 }
1087 if let Some(ref simd_ops) = self.simd_ops {
1088 simd_ops.matrices_approx_equal(&self.inner, &other.inner, tolerance)
1089 } else {
1090 BLAS::matrix_approx_equal(&self.inner, &other.inner, tolerance)
1091 }
1092 }
1093 #[must_use]
1095 pub fn analyze_structure(&self) -> MatrixStructureAnalysis {
1096 let start_time = Instant::now();
1097 let sparsity = self.nnz() as f64 / (self.shape.0 * self.shape.1) as f64;
1098 let condition_number = if self.shape.0 == self.shape.1 {
1099 BLAS::condition_number(&self.inner)
1100 } else {
1101 f64::INFINITY
1102 };
1103 let pattern = SparsityPattern::analyze(&self.inner);
1104 let compression_potential = pattern.estimate_compression_ratio();
1105 MatrixStructureAnalysis {
1106 sparsity,
1107 condition_number,
1108 is_symmetric: BLAS::is_symmetric(&self.inner, 1e-12),
1109 is_positive_definite: BLAS::is_positive_definite(&self.inner),
1110 bandwidth: pattern.bandwidth(),
1111 compression_potential,
1112 recommended_format: self.recommend_optimal_format(&pattern),
1113 analysis_time: start_time.elapsed(),
1114 }
1115 }
1116 fn recommend_optimal_format(&self, pattern: &SparsityPattern) -> SparseFormat {
1118 if pattern.is_diagonal() {
1119 SparseFormat::DIA
1120 } else if pattern.has_block_structure() {
1121 SparseFormat::BSR
1122 } else if pattern.is_gpu_suitable() {
1123 SparseFormat::GPUOptimized
1124 } else if pattern.is_simd_aligned() {
1125 SparseFormat::SIMDAligned
1126 } else if pattern.sparsity() < 0.01 {
1127 SparseFormat::COO
1128 } else if pattern.has_row_major_access() {
1129 SparseFormat::CSR
1130 } else {
1131 SparseFormat::CSC
1132 }
1133 }
1134 pub fn compress(&mut self, level: CompressionLevel) -> QuantRS2Result<f64> {
1136 let start_time = Instant::now();
1137 let original_size = self.metrics.memory_usage;
1138 let compressed = self.inner.compress(level)?;
1139 let compression_ratio = compressed.memory_footprint() as f64 / original_size as f64;
1140 self.inner = compressed;
1141 self.metrics.operation_time += start_time.elapsed();
1142 self.metrics.compression_ratio = compression_ratio;
1143 self.metrics.memory_usage = self.inner.memory_footprint();
1144 Ok(compression_ratio)
1145 }
1146 pub fn matrix_exp(&self, scale_factor: f64) -> QuantRS2Result<Self> {
1148 if self.shape.0 != self.shape.1 {
1149 return Err(QuantRS2Error::InvalidInput(
1150 "Matrix exponentiation requires square matrix".to_string(),
1151 ));
1152 }
1153 let start_time = Instant::now();
1154 let mut result = Self::new(self.shape.0, self.shape.1, SparseFormat::CSR);
1155 if let Some(ref simd_ops) = self.simd_ops {
1156 result.inner = simd_ops.matrix_exp_simd(&self.inner, scale_factor)?;
1157 result.metrics.simd_utilization = 1.0;
1158 } else {
1159 result.inner = BLAS::matrix_exp(&self.inner, scale_factor)?;
1160 }
1161 result.metrics.operation_time = start_time.elapsed();
1162 result.metrics.memory_usage = result.nnz() * std::mem::size_of::<Complex64>();
1163 result.simd_ops.clone_from(&self.simd_ops);
1164 result.buffer_pool = self.buffer_pool.clone();
1165 Ok(result)
1166 }
1167 pub const fn optimize_for_gpu(&mut self) {
1169 self.format = SparseFormat::GPUOptimized;
1170 self.metrics.compression_ratio = 0.95;
1171 self.metrics.simd_utilization = 1.0;
1172 }
1173 pub const fn optimize_for_simd(&mut self, simd_width: usize) {
1175 self.format = SparseFormat::SIMDAligned;
1176 self.metrics.simd_utilization = if simd_width >= 256 { 1.0 } else { 0.8 };
1177 self.metrics.compression_ratio = 0.90;
1178 }
1179}
1180pub struct ErrorDecomposition {
1181 pub coherent_component: f64,
1182 pub incoherent_component: f64,
1183}
1184pub struct FormatPerformancePrediction {
1185 pub best_format: SparseFormat,
1186}
1187pub struct SparseGateLibrary {
1189 gates: HashMap<String, SparseMatrix>,
1191 parameterized_gates: HashMap<String, Box<dyn Fn(&[f64]) -> SparseMatrix + Send + Sync>>,
1193 parameterized_cache: HashMap<(String, Vec<u64>), SparseMatrix>,
1195 pub metrics: LibraryMetrics,
1197}
1198impl SparseGateLibrary {
1199 #[must_use]
1201 pub fn new() -> Self {
1202 let mut library = Self {
1203 gates: HashMap::new(),
1204 parameterized_gates: HashMap::new(),
1205 parameterized_cache: HashMap::new(),
1206 metrics: LibraryMetrics::default(),
1207 };
1208 library.initialize_standard_gates();
1209 library
1210 }
1211 #[must_use]
1213 pub fn new_for_hardware(hardware_spec: HardwareSpecification) -> Self {
1214 let mut library = Self::new();
1215 if hardware_spec.has_gpu {
1216 for (gate_name, gate_matrix) in &mut library.gates {
1217 gate_matrix.format = SparseFormat::GPUOptimized;
1218 gate_matrix.optimize_for_gpu();
1219 }
1220 } else if hardware_spec.simd_width > 128 {
1221 for (gate_name, gate_matrix) in &mut library.gates {
1222 gate_matrix.format = SparseFormat::SIMDAligned;
1223 gate_matrix.optimize_for_simd(hardware_spec.simd_width);
1224 }
1225 }
1226 library
1227 }
1228 fn initialize_standard_gates(&mut self) {
1230 let mut x_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1231 x_gate.insert(0, 1, Complex64::new(1.0, 0.0));
1232 x_gate.insert(1, 0, Complex64::new(1.0, 0.0));
1233 self.gates.insert("X".to_string(), x_gate);
1234 let mut y_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1235 y_gate.insert(0, 1, Complex64::new(0.0, -1.0));
1236 y_gate.insert(1, 0, Complex64::new(0.0, 1.0));
1237 self.gates.insert("Y".to_string(), y_gate);
1238 let mut z_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1239 z_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1240 z_gate.insert(1, 1, Complex64::new(-1.0, 0.0));
1241 self.gates.insert("Z".to_string(), z_gate);
1242 let mut h_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1243 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
1244 h_gate.insert(0, 0, Complex64::new(inv_sqrt2, 0.0));
1245 h_gate.insert(0, 1, Complex64::new(inv_sqrt2, 0.0));
1246 h_gate.insert(1, 0, Complex64::new(inv_sqrt2, 0.0));
1247 h_gate.insert(1, 1, Complex64::new(-inv_sqrt2, 0.0));
1248 self.gates.insert("H".to_string(), h_gate);
1249 let mut s_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1250 s_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1251 s_gate.insert(1, 1, Complex64::new(0.0, 1.0));
1252 self.gates.insert("S".to_string(), s_gate);
1253 let mut t_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1254 t_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1255 let t_phase = std::f64::consts::PI / 4.0;
1256 t_gate.insert(1, 1, Complex64::new(t_phase.cos(), t_phase.sin()));
1257 self.gates.insert("T".to_string(), t_gate);
1258 let mut cnot_gate = SparseMatrix::new(4, 4, SparseFormat::COO);
1259 cnot_gate.insert(0, 0, Complex64::new(1.0, 0.0));
1260 cnot_gate.insert(1, 1, Complex64::new(1.0, 0.0));
1261 cnot_gate.insert(2, 3, Complex64::new(1.0, 0.0));
1262 cnot_gate.insert(3, 2, Complex64::new(1.0, 0.0));
1263 self.gates.insert("CNOT".to_string(), cnot_gate);
1264 self.initialize_parameterized_gates();
1265 }
1266 fn initialize_parameterized_gates(&mut self) {
1268 self.parameterized_gates.insert(
1269 "RZ".to_string(),
1270 Box::new(|params: &[f64]| {
1271 let theta = params[0];
1272 let mut rz_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1273 let half_theta = theta / 2.0;
1274 rz_gate.insert(0, 0, Complex64::new(half_theta.cos(), -half_theta.sin()));
1275 rz_gate.insert(1, 1, Complex64::new(half_theta.cos(), half_theta.sin()));
1276 rz_gate
1277 }),
1278 );
1279 self.parameterized_gates.insert(
1280 "RX".to_string(),
1281 Box::new(|params: &[f64]| {
1282 let theta = params[0];
1283 let mut rx_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1284 let half_theta = theta / 2.0;
1285 rx_gate.insert(0, 0, Complex64::new(half_theta.cos(), 0.0));
1286 rx_gate.insert(0, 1, Complex64::new(0.0, -half_theta.sin()));
1287 rx_gate.insert(1, 0, Complex64::new(0.0, -half_theta.sin()));
1288 rx_gate.insert(1, 1, Complex64::new(half_theta.cos(), 0.0));
1289 rx_gate
1290 }),
1291 );
1292 self.parameterized_gates.insert(
1293 "RY".to_string(),
1294 Box::new(|params: &[f64]| {
1295 let theta = params[0];
1296 let mut ry_gate = SparseMatrix::new(2, 2, SparseFormat::COO);
1297 let half_theta = theta / 2.0;
1298 ry_gate.insert(0, 0, Complex64::new(half_theta.cos(), 0.0));
1299 ry_gate.insert(0, 1, Complex64::new(-half_theta.sin(), 0.0));
1300 ry_gate.insert(1, 0, Complex64::new(half_theta.sin(), 0.0));
1301 ry_gate.insert(1, 1, Complex64::new(half_theta.cos(), 0.0));
1302 ry_gate
1303 }),
1304 );
1305 }
1306 #[must_use]
1308 pub fn get_gate(&self, name: &str) -> Option<&SparseMatrix> {
1309 self.gates.get(name)
1310 }
1311 pub fn get_parameterized_gate(
1313 &mut self,
1314 name: &str,
1315 parameters: &[f64],
1316 ) -> Option<SparseMatrix> {
1317 let param_bits: Vec<u64> = parameters.iter().map(|&p| p.to_bits()).collect();
1318 let cache_key = (name.to_string(), param_bits);
1319 if let Some(cached_matrix) = self.parameterized_cache.get(&cache_key) {
1320 self.metrics.cache_hits += 1;
1321 return Some(cached_matrix.clone());
1322 }
1323 if let Some(generator) = self.parameterized_gates.get(name) {
1324 let matrix = generator(parameters);
1325 self.metrics.cache_misses += 1;
1326 self.parameterized_cache.insert(cache_key, matrix.clone());
1327 Some(matrix)
1328 } else {
1329 None
1330 }
1331 }
1332 pub fn create_multi_qubit_gate(
1334 &self,
1335 single_qubit_gates: &[(usize, &str)],
1336 total_qubits: usize,
1337 ) -> QuantRS2Result<SparseMatrix> {
1338 let mut result = SparseMatrix::identity(1);
1339 for qubit_idx in 0..total_qubits {
1340 let gate_matrix = if let Some((_, gate_name)) =
1341 single_qubit_gates.iter().find(|(idx, _)| *idx == qubit_idx)
1342 {
1343 self.get_gate(gate_name)
1344 .ok_or_else(|| {
1345 QuantRS2Error::InvalidInput(format!("Unknown gate: {gate_name}"))
1346 })?
1347 .clone()
1348 } else {
1349 SparseMatrix::identity(2)
1350 };
1351 result = result.kron(&gate_matrix);
1352 }
1353 Ok(result)
1354 }
1355 pub fn embed_single_qubit_gate(
1357 &self,
1358 gate_name: &str,
1359 target_qubit: usize,
1360 total_qubits: usize,
1361 ) -> QuantRS2Result<SparseMatrix> {
1362 let single_qubit_gate = self
1363 .get_gate(gate_name)
1364 .ok_or_else(|| QuantRS2Error::InvalidInput(format!("Unknown gate: {gate_name}")))?;
1365 let mut result = SparseMatrix::identity(1);
1366 for qubit_idx in 0..total_qubits {
1367 if qubit_idx == target_qubit {
1368 result = result.kron(single_qubit_gate);
1369 } else {
1370 result = result.kron(&SparseMatrix::identity(2));
1371 }
1372 }
1373 Ok(result)
1374 }
1375 pub fn embed_two_qubit_gate(
1382 &self,
1383 gate_name: &str,
1384 control_qubit: usize,
1385 target_qubit: usize,
1386 total_qubits: usize,
1387 ) -> QuantRS2Result<SparseMatrix> {
1388 if control_qubit == target_qubit {
1389 return Err(QuantRS2Error::InvalidInput(
1390 "Control and target qubits must be different".to_string(),
1391 ));
1392 }
1393 if gate_name != "CNOT" {
1394 return Err(QuantRS2Error::InvalidInput(
1395 "Only CNOT supported for two-qubit embedding".to_string(),
1396 ));
1397 }
1398 if control_qubit >= total_qubits || target_qubit >= total_qubits {
1399 return Err(QuantRS2Error::InvalidInput(format!(
1400 "Qubit index out of range: control={control_qubit}, target={target_qubit}, total={total_qubits}"
1401 )));
1402 }
1403 let matrix_size = 1usize << total_qubits;
1404 let control_shift = total_qubits - 1 - control_qubit;
1405 let target_shift = total_qubits - 1 - target_qubit;
1406 let mut result = SparseMatrix::new(matrix_size, matrix_size, SparseFormat::COO);
1407 for col in 0..matrix_size {
1408 let row = if (col >> control_shift) & 1 == 1 {
1409 col ^ (1usize << target_shift)
1410 } else {
1411 col
1412 };
1413 result.insert(row, col, Complex64::new(1.0, 0.0));
1414 }
1415 Ok(result)
1416 }
1417}
1418#[derive(Debug, Clone, PartialEq, Eq, Copy)]
1420pub enum SparseFormat {
1421 COO,
1423 CSR,
1425 CSC,
1427 BSR,
1429 DIA,
1431 SciRSHybrid,
1433 GPUOptimized,
1435 SIMDAligned,
1437}
1438#[derive(Debug, Clone, Default)]
1440pub struct HardwareSpecification {
1441 pub has_gpu: bool,
1442 pub simd_width: usize,
1443 pub has_tensor_cores: bool,
1444 pub memory_bandwidth: usize,
1445 pub cache_sizes: Vec<usize>,
1446 pub num_cores: usize,
1447 pub architecture: String,
1448}
1449#[derive(Debug, Clone, Default)]
1451pub struct LibraryMetrics {
1452 pub cache_hits: usize,
1453 pub cache_misses: usize,
1454 pub cache_clears: usize,
1455 pub optimization_time: std::time::Duration,
1456 pub generation_time: std::time::Duration,
1457}
1458pub struct SpectralAnalysis {
1459 pub spectral_radius: f64,
1460 pub eigenvalue_spread: f64,
1461}
1462#[derive(Debug, Clone)]
1464pub struct GateProperties {
1465 pub is_unitary: bool,
1466 pub is_hermitian: bool,
1467 pub sparsity: f64,
1468 pub condition_number: f64,
1469 pub spectral_radius: f64,
1470 pub matrix_norm: f64,
1471 pub numerical_rank: usize,
1472 pub eigenvalue_spread: f64,
1473 pub structure_analysis: MatrixStructureAnalysis,
1474}
1475
1476const JACOBI_MAX_SWEEPS: usize = 128;
1483const JACOBI_OFFDIAG_EPS: f64 = 1e-15;
1484
1485fn densify(matrix: &SciRSSparseMatrix<Complex64>) -> (Vec<Complex64>, usize, usize) {
1488 let (rows, cols) = matrix.shape;
1489 let mut dense = vec![Complex64::new(0.0, 0.0); rows.saturating_mul(cols)];
1490 for &(r, c, v) in &matrix.data {
1491 if r < rows && c < cols {
1492 dense[r * cols + c] += v;
1493 }
1494 }
1495 (dense, rows, cols)
1496}
1497
1498fn frobenius_inner(
1500 a: &SciRSSparseMatrix<Complex64>,
1501 b: &SciRSSparseMatrix<Complex64>,
1502) -> Complex64 {
1503 let mut a_map: HashMap<(usize, usize), Complex64> = HashMap::with_capacity(a.data.len());
1504 for &(r, c, v) in &a.data {
1505 *a_map.entry((r, c)).or_insert(Complex64::new(0.0, 0.0)) += v;
1506 }
1507 let mut b_map: HashMap<(usize, usize), Complex64> = HashMap::with_capacity(b.data.len());
1508 for &(r, c, v) in &b.data {
1509 *b_map.entry((r, c)).or_insert(Complex64::new(0.0, 0.0)) += v;
1510 }
1511 let mut acc = Complex64::new(0.0, 0.0);
1512 for (key, av) in &a_map {
1513 if let Some(bv) = b_map.get(key) {
1514 acc += av.conj() * bv;
1515 }
1516 }
1517 acc
1518}
1519
1520fn jacobi_symmetric(mut a: Vec<f64>, n: usize, want_vectors: bool) -> (Vec<f64>, Vec<f64>) {
1525 if n == 0 {
1526 return (Vec::new(), Vec::new());
1527 }
1528 let mut v = if want_vectors {
1529 let mut m = vec![0.0f64; n * n];
1530 for i in 0..n {
1531 m[i * n + i] = 1.0;
1532 }
1533 m
1534 } else {
1535 Vec::new()
1536 };
1537 for _ in 0..JACOBI_MAX_SWEEPS {
1538 let mut off = 0.0;
1539 for p in 0..n {
1540 for q in (p + 1)..n {
1541 off += a[p * n + q] * a[p * n + q];
1542 }
1543 }
1544 if off.sqrt() <= JACOBI_OFFDIAG_EPS {
1545 break;
1546 }
1547 for p in 0..n {
1548 for q in (p + 1)..n {
1549 let apq = a[p * n + q];
1550 if apq.abs() <= f64::MIN_POSITIVE {
1551 continue;
1552 }
1553 let app = a[p * n + p];
1554 let aqq = a[q * n + q];
1555 let theta = (aqq - app) / (2.0 * apq);
1556 let t = if theta == 0.0 {
1557 1.0
1558 } else {
1559 let sign = if theta >= 0.0 { 1.0 } else { -1.0 };
1560 sign / (theta.abs() + (theta * theta + 1.0).sqrt())
1561 };
1562 let c = 1.0 / (t * t + 1.0).sqrt();
1563 let s = t * c;
1564 for k in 0..n {
1565 let akp = a[k * n + p];
1566 let akq = a[k * n + q];
1567 a[k * n + p] = c * akp - s * akq;
1568 a[k * n + q] = s * akp + c * akq;
1569 }
1570 for k in 0..n {
1571 let apk = a[p * n + k];
1572 let aqk = a[q * n + k];
1573 a[p * n + k] = c * apk - s * aqk;
1574 a[q * n + k] = s * apk + c * aqk;
1575 }
1576 if want_vectors {
1577 for k in 0..n {
1578 let vkp = v[k * n + p];
1579 let vkq = v[k * n + q];
1580 v[k * n + p] = c * vkp - s * vkq;
1581 v[k * n + q] = s * vkp + c * vkq;
1582 }
1583 }
1584 }
1585 }
1586 }
1587 let eig = (0..n).map(|i| a[i * n + i]).collect();
1588 (eig, v)
1589}
1590
1591fn hermitian_real_embed(g: &[Complex64], n: usize) -> Vec<f64> {
1595 let m = 2 * n;
1596 let mut r = vec![0.0f64; m * m];
1597 for i in 0..n {
1598 for j in 0..n {
1599 let gij = g[i * n + j];
1600 r[i * m + j] = gij.re;
1601 r[i * m + (j + n)] = -gij.im;
1602 r[(i + n) * m + j] = gij.im;
1603 r[(i + n) * m + (j + n)] = gij.re;
1604 }
1605 }
1606 r
1607}
1608
1609fn hermitian_eigenvalues_dense(g: &[Complex64], n: usize) -> Vec<f64> {
1612 if n == 0 {
1613 return Vec::new();
1614 }
1615 let r = hermitian_real_embed(g, n);
1616 let (mut eig2, _) = jacobi_symmetric(r, 2 * n, false);
1617 eig2.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
1618 (0..n).map(|k| eig2[2 * k]).collect()
1620}
1621
1622fn singular_values_dense(dense: &[Complex64], rows: usize, cols: usize) -> Vec<f64> {
1625 if rows == 0 || cols == 0 {
1626 return Vec::new();
1627 }
1628 let (gram, dim) = if cols <= rows {
1629 let mut g = vec![Complex64::new(0.0, 0.0); cols * cols];
1631 for k in 0..rows {
1632 let base = k * cols;
1633 for i in 0..cols {
1634 let mki = dense[base + i].conj();
1635 for j in 0..cols {
1636 g[i * cols + j] += mki * dense[base + j];
1637 }
1638 }
1639 }
1640 (g, cols)
1641 } else {
1642 let mut g = vec![Complex64::new(0.0, 0.0); rows * rows];
1644 for i in 0..rows {
1645 for j in 0..rows {
1646 let mut acc = Complex64::new(0.0, 0.0);
1647 for k in 0..cols {
1648 acc += dense[i * cols + k] * dense[j * cols + k].conj();
1649 }
1650 g[i * rows + j] = acc;
1651 }
1652 }
1653 (g, rows)
1654 };
1655 hermitian_eigenvalues_dense(&gram, dim)
1656 .into_iter()
1657 .map(|e| e.max(0.0).sqrt())
1658 .collect()
1659}
1660
1661fn dense_matvec(m: &[Complex64], n: usize, x: &[Complex64]) -> Vec<Complex64> {
1663 let mut y = vec![Complex64::new(0.0, 0.0); n];
1664 for i in 0..n {
1665 let base = i * n;
1666 let mut acc = Complex64::new(0.0, 0.0);
1667 for j in 0..n {
1668 acc += m[base + j] * x[j];
1669 }
1670 y[i] = acc;
1671 }
1672 y
1673}
1674
1675fn cvec_norm(x: &[Complex64]) -> f64 {
1677 x.iter().map(|v| v.norm_sqr()).sum::<f64>().sqrt()
1678}
1679
1680fn seed_vector(n: usize) -> Vec<Complex64> {
1682 (0..n)
1683 .map(|i| Complex64::new(1.0 + (i as f64) * 0.137, 0.31 - (i as f64) * 0.057))
1684 .collect()
1685}
1686
1687fn spectral_radius_dense(m: &[Complex64], n: usize) -> f64 {
1691 if n == 0 {
1692 return 0.0;
1693 }
1694 let mut x = seed_vector(n);
1695 let norm0 = cvec_norm(&x);
1696 if norm0 == 0.0 {
1697 return 0.0;
1698 }
1699 for v in &mut x {
1700 *v /= norm0;
1701 }
1702 let burn_in = 40usize;
1703 let iters = 400usize;
1704 let mut log_sum = 0.0;
1705 let mut count = 0usize;
1706 for iter in 0..iters {
1707 let y = dense_matvec(m, n, &x);
1708 let ny = cvec_norm(&y);
1709 if ny <= 1e-300 {
1710 return 0.0;
1711 }
1712 if iter >= burn_in {
1713 log_sum += ny.ln();
1714 count += 1;
1715 }
1716 for i in 0..n {
1717 x[i] = y[i] / ny;
1718 }
1719 }
1720 if count == 0 {
1721 0.0
1722 } else {
1723 (log_sum / count as f64).exp()
1724 }
1725}
1726
1727fn lu_factor(dense: &[Complex64], n: usize) -> Option<(Vec<Complex64>, Vec<usize>)> {
1731 let mut a = dense.to_vec();
1732 let mut piv: Vec<usize> = (0..n).collect();
1733 for k in 0..n {
1734 let mut p = k;
1735 let mut maxv = a[k * n + k].norm();
1736 for i in (k + 1)..n {
1737 let v = a[i * n + k].norm();
1738 if v > maxv {
1739 maxv = v;
1740 p = i;
1741 }
1742 }
1743 if maxv <= 1e-300 {
1744 return None;
1745 }
1746 if p != k {
1747 for j in 0..n {
1748 a.swap(k * n + j, p * n + j);
1749 }
1750 piv.swap(k, p);
1751 }
1752 let pivot = a[k * n + k];
1753 for i in (k + 1)..n {
1754 let factor = a[i * n + k] / pivot;
1755 a[i * n + k] = factor;
1756 for j in (k + 1)..n {
1757 let ajk = a[k * n + j];
1758 a[i * n + j] -= factor * ajk;
1759 }
1760 }
1761 }
1762 Some((a, piv))
1763}
1764
1765fn lu_solve(lu: &(Vec<Complex64>, Vec<usize>), n: usize, b: &[Complex64]) -> Vec<Complex64> {
1767 let (a, piv) = lu;
1768 let mut x = vec![Complex64::new(0.0, 0.0); n];
1769 for i in 0..n {
1770 x[i] = b[piv[i]];
1771 }
1772 for i in 0..n {
1773 let mut sum = x[i];
1774 for j in 0..i {
1775 sum -= a[i * n + j] * x[j];
1776 }
1777 x[i] = sum;
1778 }
1779 for i in (0..n).rev() {
1780 let mut sum = x[i];
1781 for j in (i + 1)..n {
1782 sum -= a[i * n + j] * x[j];
1783 }
1784 x[i] = sum / a[i * n + i];
1785 }
1786 x
1787}
1788
1789fn min_eig_magnitude_dense(m: &[Complex64], n: usize) -> f64 {
1792 if n == 0 {
1793 return 0.0;
1794 }
1795 let Some(lu) = lu_factor(m, n) else {
1796 return 0.0;
1797 };
1798 let mut x = seed_vector(n);
1799 let norm0 = cvec_norm(&x);
1800 if norm0 == 0.0 {
1801 return 0.0;
1802 }
1803 for v in &mut x {
1804 *v /= norm0;
1805 }
1806 let burn_in = 40usize;
1807 let iters = 400usize;
1808 let mut log_sum = 0.0;
1809 let mut count = 0usize;
1810 for iter in 0..iters {
1811 let y = lu_solve(&lu, n, &x);
1812 let ny = cvec_norm(&y);
1813 if ny <= 1e-300 {
1814 return 0.0;
1815 }
1816 if iter >= burn_in {
1817 log_sum += ny.ln();
1818 count += 1;
1819 }
1820 for i in 0..n {
1821 x[i] = y[i] / ny;
1822 }
1823 }
1824 let inv_growth = if count == 0 {
1826 0.0
1827 } else {
1828 (log_sum / count as f64).exp()
1829 };
1830 if inv_growth <= 1e-300 {
1831 0.0
1832 } else {
1833 1.0 / inv_growth
1834 }
1835}
1836
1837fn normal_eigenvalues_dense(w: &[Complex64], n: usize) -> Vec<Complex64> {
1843 if n == 0 {
1844 return Vec::new();
1845 }
1846 let gamma = 0.786_151_377_757_423_f64;
1847 let mut h = vec![Complex64::new(0.0, 0.0); n * n];
1848 for i in 0..n {
1849 for j in 0..n {
1850 let wij = w[i * n + j];
1851 let wji = w[j * n + i].conj();
1852 let hermitian = (wij + wji) * Complex64::new(0.5, 0.0);
1853 let anti = (wij - wji) / Complex64::new(0.0, 2.0);
1854 h[i * n + j] = hermitian + anti * Complex64::new(gamma, 0.0);
1855 }
1856 }
1857 let r = hermitian_real_embed(&h, n);
1858 let (eig2, vecs) = jacobi_symmetric(r, 2 * n, true);
1859 let mut idx: Vec<usize> = (0..2 * n).collect();
1860 idx.sort_by(|&x, &y| {
1861 eig2[y]
1862 .partial_cmp(&eig2[x])
1863 .unwrap_or(std::cmp::Ordering::Equal)
1864 });
1865 let m = 2 * n;
1866 let mut out = Vec::with_capacity(n);
1867 let mut k = 0usize;
1868 while k < 2 * n && out.len() < n {
1869 let col = idx[k];
1870 let mut u = vec![Complex64::new(0.0, 0.0); n];
1871 for (row_i, u_val) in u.iter_mut().enumerate() {
1872 let p = vecs[row_i * m + col];
1873 let q = vecs[(row_i + n) * m + col];
1874 *u_val = Complex64::new(p, q);
1875 }
1876 let wu = dense_matvec(w, n, &u);
1877 let mut num = Complex64::new(0.0, 0.0);
1878 let mut den = 0.0;
1879 for i in 0..n {
1880 num += u[i].conj() * wu[i];
1881 den += u[i].norm_sqr();
1882 }
1883 out.push(if den > 1e-300 {
1884 num / Complex64::new(den, 0.0)
1885 } else {
1886 Complex64::new(0.0, 0.0)
1887 });
1888 k += 2;
1889 }
1890 out
1891}
1892
1893fn hull_diamond_distance(eig: &[Complex64]) -> f64 {
1897 let mut angles: Vec<f64> = eig
1898 .iter()
1899 .filter(|z| z.norm() > 1e-12)
1900 .map(|z| z.arg())
1901 .collect();
1902 if angles.is_empty() {
1903 return 0.0;
1904 }
1905 angles.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1906 let m = angles.len();
1907 let mut max_gap = angles[0] + std::f64::consts::TAU - angles[m - 1];
1908 for k in 1..m {
1909 let gap = angles[k] - angles[k - 1];
1910 if gap > max_gap {
1911 max_gap = gap;
1912 }
1913 }
1914 if max_gap <= std::f64::consts::PI {
1915 2.0
1916 } else {
1917 let spanned = std::f64::consts::TAU - max_gap;
1918 let delta = (spanned / 2.0).cos();
1919 2.0 * (1.0 - delta * delta).max(0.0).sqrt()
1920 }
1921}
1922
1923fn recip_factorial(k: u32) -> f64 {
1925 let mut f = 1.0_f64;
1926 for i in 1..=k {
1927 f *= f64::from(i);
1928 }
1929 1.0 / f
1930}
1931
1932fn dense_identity(n: usize) -> Vec<Complex64> {
1934 let mut m = vec![Complex64::new(0.0, 0.0); n * n];
1935 for i in 0..n {
1936 m[i * n + i] = Complex64::new(1.0, 0.0);
1937 }
1938 m
1939}
1940
1941fn dense_matmul(a: &[Complex64], b: &[Complex64], n: usize) -> Vec<Complex64> {
1943 let mut c = vec![Complex64::new(0.0, 0.0); n * n];
1944 for i in 0..n {
1945 for k in 0..n {
1946 let a_ik = a[i * n + k];
1947 if a_ik.norm_sqr() == 0.0 {
1948 continue;
1949 }
1950 let brow = k * n;
1951 let crow = i * n;
1952 for j in 0..n {
1953 c[crow + j] += a_ik * b[brow + j];
1954 }
1955 }
1956 }
1957 c
1958}
1959
1960fn dense_inf_norm(a: &[Complex64], n: usize) -> f64 {
1962 let mut max_row = 0.0_f64;
1963 for i in 0..n {
1964 let base = i * n;
1965 let row_sum: f64 = (0..n).map(|j| a[base + j].norm()).sum();
1966 if row_sum > max_row {
1967 max_row = row_sum;
1968 }
1969 }
1970 max_row
1971}
1972
1973fn expm_dense(m: &[Complex64], n: usize, scale: f64) -> Vec<Complex64> {
1977 if n == 0 {
1978 return Vec::new();
1979 }
1980 let scale_c = Complex64::new(scale, 0.0);
1981 let a: Vec<Complex64> = m.iter().map(|&v| v * scale_c).collect();
1982 let norm = dense_inf_norm(&a, n);
1983 let s = if norm <= 0.5 {
1984 0u32
1985 } else {
1986 ((norm.log2().ceil().max(0.0) as u32) + 1).min(60)
1987 };
1988 let scaling = Complex64::new(2.0_f64.powi(-(s as i32)), 0.0);
1989 let a_scaled: Vec<Complex64> = a.iter().map(|&v| v * scaling).collect();
1990 let mut result = dense_identity(n);
1991 let mut term = dense_identity(n);
1992 for k in 1..=18u32 {
1993 term = dense_matmul(&term, &a_scaled, n);
1994 let inv_fact = Complex64::new(recip_factorial(k), 0.0);
1995 for (r, t) in result.iter_mut().zip(term.iter()) {
1996 *r += *t * inv_fact;
1997 }
1998 }
1999 for _ in 0..s {
2000 result = dense_matmul(&result, &result, n);
2001 }
2002 result
2003}