1use crate::{
7 error::{QuantRS2Error, QuantRS2Result},
8 gate::GateOp,
9 matrix_ops::matrices_approx_equal,
10 qubit::QubitId,
11};
12use scirs2_core::ndarray::{Array2, ArrayView2};
13use scirs2_core::Complex64;
14use scirs2_linalg::tensor_contraction::tucker::tucker_decomposition;
18use crate::linalg_stubs::{randomized_svd, truncated_svd};
20use crate::optimization_stubs::{differential_evolution, DifferentialEvolutionOptions};
21use std::any::Any;
22use std::collections::HashMap;
23
24#[derive(Debug, Clone)]
26pub struct CompressionConfig {
27 pub tolerance: f64,
29 pub max_rank: Option<usize>,
31 pub use_randomized: bool,
33 pub max_iterations: usize,
35 pub num_threads: Option<usize>,
37}
38
39impl Default for CompressionConfig {
40 fn default() -> Self {
41 Self {
42 tolerance: 1e-10,
43 max_rank: None,
44 use_randomized: true,
45 max_iterations: 1000,
46 num_threads: None,
47 }
48 }
49}
50
51pub struct GateSequenceCompressor {
53 config: CompressionConfig,
54 compression_cache: HashMap<u64, CompressedGate>,
56}
57
58#[derive(Debug, Clone)]
60pub enum CompressedGate {
61 LowRank {
63 left: Array2<Complex64>,
64 right: Array2<Complex64>,
65 rank: usize,
66 },
67 Tucker {
69 core: Array2<Complex64>,
70 factors: Vec<Array2<Complex64>>,
71 },
72 Parameterized {
74 gate_type: String,
75 parameters: Vec<f64>,
76 qubits: Vec<QubitId>,
77 },
78 RuntimeCompressed {
80 compressed_data: Vec<u8>,
81 compression_type: CompressionType,
82 original_size: usize,
83 gate_metadata: GateMetadata,
84 },
85 Original(Box<dyn GateOp>),
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum CompressionType {
92 None,
94 Zlib,
96 LZ4,
98 Huffman,
100 QuantumOptimized,
102}
103
104#[derive(Debug, Clone)]
106pub struct GateMetadata {
107 pub name: String,
109 pub num_qubits: usize,
111 pub qubits: Vec<QubitId>,
113 pub matrix_dims: (usize, usize),
115 pub sparsity_ratio: f64,
117 pub is_unitary: bool,
119}
120
121impl GateSequenceCompressor {
122 pub fn new(config: CompressionConfig) -> Self {
124 Self {
125 config,
126 compression_cache: HashMap::new(),
127 }
128 }
129
130 pub fn compress_gate(&mut self, gate: &dyn GateOp) -> QuantRS2Result<CompressedGate> {
132 let matrix_vec = gate.matrix()?;
133
134 let n = (matrix_vec.len() as f64).sqrt() as usize;
136 let mut matrix = Array2::zeros((n, n));
137 for j in 0..n {
138 for i in 0..n {
139 matrix[(i, j)] = matrix_vec[j * n + i];
140 }
141 }
142
143 let matrix_view = matrix.view();
144 let hash = self.compute_matrix_hash(&matrix_view);
145
146 if let Some(compressed) = self.compression_cache.get(&hash) {
148 return Ok(compressed.clone());
149 }
150
151 let compressed = if let Some(low_rank) = self.try_low_rank_approximation(&matrix_view)? {
153 low_rank
154 } else if let Some(tucker) = self.try_tucker_decomposition(&matrix_view)? {
155 tucker
156 } else if let Some(param) = self.try_parameterized_compression(gate)? {
157 param
158 } else if let Some(runtime_compressed) = self.try_runtime_compression(gate)? {
159 runtime_compressed
160 } else {
161 CompressedGate::Original(gate.clone_gate())
162 };
163
164 self.compression_cache.insert(hash, compressed.clone());
166
167 Ok(compressed)
168 }
169
170 pub fn compress_sequence(
172 &mut self,
173 gates: &[Box<dyn GateOp>],
174 ) -> QuantRS2Result<Vec<CompressedGate>> {
175 if let Some(threads) = self.config.num_threads {
177 use scirs2_core::parallel_ops::ThreadPoolBuilder;
178 ThreadPoolBuilder::new()
179 .num_threads(threads)
180 .build_global()
181 .map_err(|e| QuantRS2Error::InvalidInput(e.to_string()))?;
182 }
183
184 let merged = self.merge_adjacent_gates(gates)?;
186
187 let compressed: Result<Vec<_>, _> = merged
189 .iter()
190 .map(|gate| self.compress_gate(gate.as_ref()))
191 .collect();
192
193 compressed
194 }
195
196 fn try_low_rank_approximation(
198 &self,
199 matrix: &ArrayView2<Complex64>,
200 ) -> QuantRS2Result<Option<CompressedGate>> {
201 let (rows, cols) = matrix.dim();
202 if rows != cols || rows < 4 {
203 return Ok(None);
205 }
206
207 let real_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].re);
209 let imag_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].im);
210
211 let target_rank = self.config.max_rank.unwrap_or(rows / 2);
213
214 let (u_real, s_real, vt_real) = if self.config.use_randomized {
216 randomized_svd(&real_part.view(), target_rank, Some(10), Some(2), None)
217 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
218 } else {
219 truncated_svd(&real_part.view(), target_rank, None)
220 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
221 };
222
223 let (u_imag, s_imag, vt_imag) = if self.config.use_randomized {
224 randomized_svd(&imag_part.view(), target_rank, Some(10), Some(2), None)
225 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
226 } else {
227 truncated_svd(&imag_part.view(), target_rank, None)
228 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
229 };
230
231 let effective_rank = self.find_effective_rank(&s_real, &s_imag)?;
233
234 if effective_rank >= rows * 3 / 4 {
235 return Ok(None);
237 }
238
239 let left = self.combine_complex(&u_real, &u_imag, effective_rank)?;
241 let right = self.combine_complex_with_singular(
242 &vt_real,
243 &vt_imag,
244 &s_real,
245 &s_imag,
246 effective_rank,
247 )?;
248
249 let approx = left.dot(&right.t());
251 if !matrices_approx_equal(&approx.view(), matrix, self.config.tolerance) {
252 return Ok(None);
253 }
254
255 Ok(Some(CompressedGate::LowRank {
256 left,
257 right,
258 rank: effective_rank,
259 }))
260 }
261
262 fn try_tucker_decomposition(
268 &self,
269 matrix: &ArrayView2<Complex64>,
270 ) -> QuantRS2Result<Option<CompressedGate>> {
271 let (rows, cols) = matrix.dim();
272
273 if rows < 4 || cols < 4 || rows != cols {
275 return Ok(None);
276 }
277
278 let target_rank = self.config.max_rank.unwrap_or(rows / 2).min(rows - 1);
280 let ranks = vec![target_rank, target_rank];
281
282 let real_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].re);
284 let imag_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].im);
285
286 let tucker_real = tucker_decomposition(&real_part.view(), &ranks).map_err(|e| {
288 QuantRS2Error::InvalidInput(format!("Tucker decomposition (real) failed: {}", e))
289 })?;
290
291 let tucker_imag = tucker_decomposition(&imag_part.view(), &ranks).map_err(|e| {
293 QuantRS2Error::InvalidInput(format!("Tucker decomposition (imag) failed: {}", e))
294 })?;
295
296 let core_real = tucker_real
298 .core
299 .into_dimensionality::<scirs2_core::ndarray::Ix2>()
300 .map_err(|e| {
301 QuantRS2Error::InvalidInput(format!("Failed to convert real core: {}", e))
302 })?;
303 let core_imag = tucker_imag
304 .core
305 .into_dimensionality::<scirs2_core::ndarray::Ix2>()
306 .map_err(|e| {
307 QuantRS2Error::InvalidInput(format!("Failed to convert imag core: {}", e))
308 })?;
309
310 let core_2d = Array2::from_shape_fn(core_real.dim(), |(i, j)| {
312 Complex64::new(core_real[(i, j)], core_imag[(i, j)])
313 });
314
315 let mut factors = Vec::new();
318 for i in 0..tucker_real.factors.len() {
319 let factor_real = &tucker_real.factors[i];
320 let factor_imag = &tucker_imag.factors[i];
321 let combined_factor = Array2::from_shape_fn(factor_real.dim(), |(r, c)| {
322 Complex64::new(factor_real[(r, c)], factor_imag[(r, c)])
323 });
324 factors.push(combined_factor);
325 }
326
327 let original_params = rows * cols * 2; let compressed_params =
330 (core_2d.len() * 2) + factors.iter().map(|f| f.len() * 2).sum::<usize>();
331
332 if compressed_params >= original_params * 3 / 4 {
333 return Ok(None);
335 }
336
337 let reconstructed = self.reconstruct_tucker(&core_2d, &factors)?;
339 if !matrices_approx_equal(&reconstructed.view(), matrix, self.config.tolerance) {
340 return Ok(None);
341 }
342
343 Ok(Some(CompressedGate::Tucker {
344 core: core_2d,
345 factors,
346 }))
347 }
348
349 fn reconstruct_tucker(
351 &self,
352 core: &Array2<Complex64>,
353 factors: &[Array2<Complex64>],
354 ) -> QuantRS2Result<Array2<Complex64>> {
355 if factors.len() != 2 {
356 return Err(QuantRS2Error::InvalidInput(
357 "Tucker decomposition requires exactly 2 factors for 2D matrices".to_string(),
358 ));
359 }
360
361 let temp = core.dot(&factors[1].t());
364 let result = factors[0].dot(&temp);
366
367 Ok(result)
368 }
369
370 fn try_parameterized_compression(
372 &self,
373 gate: &dyn GateOp,
374 ) -> QuantRS2Result<Option<CompressedGate>> {
375 let matrix_vec = gate.matrix()?;
380 let n = (matrix_vec.len() as f64).sqrt() as usize;
381
382 if n > 4 {
383 return Ok(None);
385 }
386
387 let mut target_matrix = Array2::zeros((n, n));
389 for j in 0..n {
390 for i in 0..n {
391 target_matrix[(i, j)] = matrix_vec[j * n + i];
392 }
393 }
394
395 let gate_type = self.identify_gate_type(gate);
396
397 let dim = match gate_type.as_str() {
399 "rotation" => 3, "phase" => 1, _ => 6, };
403 let bounds = vec![(Some(-std::f64::consts::PI), Some(std::f64::consts::PI)); dim];
404
405 let target_matrix_clone = target_matrix.clone();
407 let gate_type_clone = gate_type.clone();
408 let _tolerance = self.config.tolerance;
409
410 let objective = move |x: &scirs2_core::ndarray::ArrayView1<f64>| -> f64 {
412 let params: Vec<f64> = x.iter().cloned().collect();
413
414 let gate_matrix = match gate_type_clone.as_str() {
416 "rotation" => Array2::eye(target_matrix_clone.dim().0), "phase" => {
418 let mut matrix = Array2::eye(target_matrix_clone.dim().0);
419 if !params.is_empty() {
420 let phase = Complex64::from_polar(1.0, params[0]);
421 let n = matrix.dim().0;
422 matrix[(n - 1, n - 1)] = phase;
423 }
424 matrix
425 }
426 _ => Array2::eye(target_matrix_clone.dim().0), };
428
429 let diff = &target_matrix_clone - &gate_matrix;
431 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
432 };
433
434 let mut options = DifferentialEvolutionOptions::default();
436 options.popsize = 50;
437 options.maxiter = self.config.max_iterations;
438 options.tol = self.config.tolerance;
439
440 let de_bounds: Vec<(f64, f64)> = bounds
441 .into_iter()
442 .map(|(low, high)| {
443 (
444 low.unwrap_or(-std::f64::consts::PI),
445 high.unwrap_or(std::f64::consts::PI),
446 )
447 })
448 .collect();
449
450 let result =
451 differential_evolution(objective, &de_bounds, Some(options), None).map_err(|e| {
452 QuantRS2Error::InvalidInput(format!("Parameter optimization failed: {:?}", e))
453 })?;
454
455 if result.fun > self.config.tolerance {
456 return Ok(None);
458 }
459
460 Ok(Some(CompressedGate::Parameterized {
461 gate_type,
462 parameters: result.x.to_vec(),
463 qubits: vec![], }))
465 }
466
467 fn merge_adjacent_gates(
469 &self,
470 gates: &[Box<dyn GateOp>],
471 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
472 let mut merged = Vec::new();
473 let mut i = 0;
474
475 while i < gates.len() {
476 if i + 1 < gates.len() {
477 if self.can_merge(gates[i].as_ref(), gates[i + 1].as_ref()) {
479 let combined =
481 self.merge_two_gates(gates[i].as_ref(), gates[i + 1].as_ref())?;
482 merged.push(combined);
483 i += 2;
484 } else {
485 merged.push(gates[i].clone_gate());
486 i += 1;
487 }
488 } else {
489 merged.push(gates[i].clone_gate());
490 i += 1;
491 }
492 }
493
494 Ok(merged)
495 }
496
497 fn can_merge(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
499 gate1.name() == gate2.name()
506 }
507
508 fn merge_two_gates(
510 &self,
511 gate1: &dyn GateOp,
512 gate2: &dyn GateOp,
513 ) -> QuantRS2Result<Box<dyn GateOp>> {
514 let matrix1_vec = gate1.matrix()?;
516 let matrix2_vec = gate2.matrix()?;
517
518 let n = (matrix1_vec.len() as f64).sqrt() as usize;
520 let mut matrix1 = Array2::zeros((n, n));
521 let mut matrix2 = Array2::zeros((n, n));
522
523 for j in 0..n {
524 for i in 0..n {
525 matrix1[(i, j)] = matrix1_vec[j * n + i];
526 matrix2[(i, j)] = matrix2_vec[j * n + i];
527 }
528 }
529
530 let combined_matrix = matrix2.dot(&matrix1);
532
533 Ok(Box::new(CustomGate::new(
535 format!("{}_{}_merged", gate1.name(), gate2.name()),
536 combined_matrix,
537 )))
538 }
539
540 fn compute_matrix_hash(&self, matrix: &ArrayView2<Complex64>) -> u64 {
542 use std::collections::hash_map::DefaultHasher;
543 use std::hash::{Hash, Hasher};
544
545 let mut hasher = DefaultHasher::new();
546 for elem in matrix.iter() {
547 elem.re.to_bits().hash(&mut hasher);
549 elem.im.to_bits().hash(&mut hasher);
550 }
551 hasher.finish()
552 }
553
554 fn find_effective_rank(
556 &self,
557 s_real: &scirs2_core::ndarray::Array1<f64>,
558 s_imag: &scirs2_core::ndarray::Array1<f64>,
559 ) -> QuantRS2Result<usize> {
560 let max_singular = s_real
561 .iter()
562 .chain(s_imag.iter())
563 .map(|s| s.abs())
564 .fold(0.0, f64::max);
565
566 let threshold = max_singular * self.config.tolerance;
567
568 let rank = s_real
569 .iter()
570 .zip(s_imag.iter())
571 .take_while(|(sr, si)| sr.abs() > threshold || si.abs() > threshold)
572 .count();
573
574 Ok(rank.max(1))
575 }
576
577 fn combine_complex(
579 &self,
580 real: &Array2<f64>,
581 imag: &Array2<f64>,
582 rank: usize,
583 ) -> QuantRS2Result<Array2<Complex64>> {
584 let (rows, _) = real.dim();
585 let result = Array2::from_shape_fn((rows, rank), |(i, j)| {
586 Complex64::new(real[(i, j)], imag[(i, j)])
587 });
588 Ok(result)
589 }
590
591 fn combine_complex_with_singular(
593 &self,
594 vt_real: &Array2<f64>,
595 vt_imag: &Array2<f64>,
596 s_real: &scirs2_core::ndarray::Array1<f64>,
597 s_imag: &scirs2_core::ndarray::Array1<f64>,
598 rank: usize,
599 ) -> QuantRS2Result<Array2<Complex64>> {
600 let (_, cols) = vt_real.dim();
601 let result = Array2::from_shape_fn((rank, cols), |(i, j)| {
602 let s = Complex64::new(s_real[i], s_imag[i]);
603 let v = Complex64::new(vt_real[(i, j)], vt_imag[(i, j)]);
604 s * v
605 });
606 Ok(result)
607 }
608
609 #[allow(dead_code)]
611 fn tensor_to_complex_matrix(&self, tensor: &[f64]) -> QuantRS2Result<Array2<Complex64>> {
612 let size = (tensor.len() / 2) as f64;
613 let dim = size.sqrt() as usize;
614
615 let mut matrix = Array2::zeros((dim, dim));
616 for i in 0..dim {
617 for j in 0..dim {
618 let idx = (i * dim + j) * 2;
619 matrix[(i, j)] = Complex64::new(tensor[idx], tensor[idx + 1]);
620 }
621 }
622
623 Ok(matrix)
624 }
625
626 #[allow(dead_code)]
628 fn tensor_to_complex_matrix_from_array(
629 &self,
630 tensor: &scirs2_core::ndarray::ArrayD<f64>,
631 ) -> QuantRS2Result<Array2<Complex64>> {
632 let elements: Vec<f64> = tensor.iter().cloned().collect();
634 let size = elements.len() as f64;
635 let dim = size.sqrt() as usize;
636
637 if dim * dim != elements.len() {
638 let dim = (size.sqrt().ceil()) as usize;
640 let mut matrix = Array2::zeros((dim, dim));
641 for (idx, &val) in elements.iter().enumerate() {
642 let i = idx / dim;
643 let j = idx % dim;
644 if i < dim && j < dim {
645 matrix[(i, j)] = Complex64::new(val, 0.0);
646 }
647 }
648 Ok(matrix)
649 } else {
650 let mut matrix = Array2::zeros((dim, dim));
651 for i in 0..dim {
652 for j in 0..dim {
653 let idx = i * dim + j;
654 matrix[(i, j)] = Complex64::new(elements[idx], 0.0);
655 }
656 }
657 Ok(matrix)
658 }
659 }
660
661 fn identify_gate_type(&self, gate: &dyn GateOp) -> String {
663 let name = gate.name();
665 if name.contains("rot") || name.contains("Rot") {
666 "rotation".to_string()
667 } else if name.contains("phase") || name.contains("Phase") {
668 "phase".to_string()
669 } else {
670 "general".to_string()
671 }
672 }
673
674 #[allow(dead_code)]
676 fn evaluate_gate_parameters(
677 &self,
678 target: &Array2<Complex64>,
679 gate_type: &str,
680 params: &[f64],
681 ) -> f64 {
682 let gate_matrix = match gate_type {
684 "rotation" => self.rotation_matrix_from_params(params, target.dim().0),
685 "phase" => self.phase_matrix_from_params(params, target.dim().0),
686 _ => self.general_matrix_from_params(params, target.dim().0),
687 };
688
689 let diff = target - &gate_matrix;
691 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
692 }
693
694 fn rotation_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
695 Array2::eye(dim)
698 }
699
700 fn phase_matrix_from_params(&self, params: &[f64], dim: usize) -> Array2<Complex64> {
701 let mut matrix = Array2::eye(dim);
702 if !params.is_empty() {
703 let phase = Complex64::from_polar(1.0, params[0]);
704 matrix[(dim - 1, dim - 1)] = phase;
705 }
706 matrix
707 }
708
709 #[allow(dead_code)]
710 fn general_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
711 Array2::eye(dim)
713 }
714
715 fn try_runtime_compression(&self, gate: &dyn GateOp) -> QuantRS2Result<Option<CompressedGate>> {
717 let matrix_vec = gate.matrix()?;
718 let n = (matrix_vec.len() as f64).sqrt() as usize;
719
720 let metadata = GateMetadata {
722 name: gate.name().to_string(),
723 num_qubits: gate.num_qubits(),
724 qubits: gate.qubits(),
725 matrix_dims: (n, n),
726 sparsity_ratio: self.calculate_sparsity_ratio(&matrix_vec),
727 is_unitary: self.check_unitary(&matrix_vec, n),
728 };
729
730 let matrix_bytes = self.serialize_matrix(&matrix_vec)?;
732
733 let best_compression = self.find_best_compression(&matrix_bytes, &metadata)?;
735
736 if best_compression.compression_ratio < 0.8 {
738 Ok(Some(CompressedGate::RuntimeCompressed {
739 compressed_data: best_compression.data,
740 compression_type: best_compression.compression_type,
741 original_size: matrix_bytes.len(),
742 gate_metadata: metadata,
743 }))
744 } else {
745 Ok(None)
746 }
747 }
748
749 fn calculate_sparsity_ratio(&self, matrix: &[Complex64]) -> f64 {
751 let zero_count = matrix
752 .iter()
753 .filter(|&c| c.norm() < self.config.tolerance)
754 .count();
755 zero_count as f64 / matrix.len() as f64
756 }
757
758 fn check_unitary(&self, matrix: &[Complex64], n: usize) -> bool {
760 if n > 8 {
763 return false; }
765
766 let mut u = Array2::zeros((n, n));
768 for j in 0..n {
769 for i in 0..n {
770 u[(i, j)] = matrix[j * n + i];
771 }
772 }
773
774 let u_dagger = u.t().mapv(|c| c.conj());
776 let product = u_dagger.dot(&u);
777
778 let identity = Array2::<Complex64>::eye(n);
780 let diff = &product - &identity;
781 let frobenius_norm: f64 = diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
782
783 frobenius_norm < self.config.tolerance
784 }
785
786 fn serialize_matrix(&self, matrix: &[Complex64]) -> QuantRS2Result<Vec<u8>> {
788 let mut bytes = Vec::with_capacity(matrix.len() * 16); for &complex in matrix {
791 bytes.extend_from_slice(&complex.re.to_le_bytes());
792 bytes.extend_from_slice(&complex.im.to_le_bytes());
793 }
794
795 Ok(bytes)
796 }
797
798 fn find_best_compression(
800 &self,
801 data: &[u8],
802 metadata: &GateMetadata,
803 ) -> QuantRS2Result<CompressionResult> {
804 let mut best_compression = CompressionResult {
805 data: data.to_vec(),
806 compression_type: CompressionType::None,
807 compression_ratio: 1.0,
808 };
809
810 if let Ok(zlib_compressed) = self.compress_zlib(data) {
812 let ratio = zlib_compressed.len() as f64 / data.len() as f64;
813 if ratio < best_compression.compression_ratio {
814 best_compression = CompressionResult {
815 data: zlib_compressed,
816 compression_type: CompressionType::Zlib,
817 compression_ratio: ratio,
818 };
819 }
820 }
821
822 if let Ok(lz4_compressed) = self.compress_lz4(data) {
824 let ratio = lz4_compressed.len() as f64 / data.len() as f64;
825 if ratio < best_compression.compression_ratio {
826 best_compression = CompressionResult {
827 data: lz4_compressed,
828 compression_type: CompressionType::LZ4,
829 compression_ratio: ratio,
830 };
831 }
832 }
833
834 if metadata.sparsity_ratio > 0.3 {
836 if let Ok(quantum_compressed) = self.compress_quantum_optimized(data, metadata) {
837 let ratio = quantum_compressed.len() as f64 / data.len() as f64;
838 if ratio < best_compression.compression_ratio {
839 best_compression = CompressionResult {
840 data: quantum_compressed,
841 compression_type: CompressionType::QuantumOptimized,
842 compression_ratio: ratio,
843 };
844 }
845 }
846 }
847
848 Ok(best_compression)
849 }
850
851 fn compress_zlib(&self, _data: &[u8]) -> QuantRS2Result<Vec<u8>> {
853 #[cfg(feature = "compression")]
854 {
855 use std::io::Write;
856 let mut encoder =
857 flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
858 encoder.write_all(_data).map_err(|e| {
859 QuantRS2Error::RuntimeError(format!("Zlib compression failed: {}", e))
860 })?;
861
862 encoder
863 .finish()
864 .map_err(|e| QuantRS2Error::RuntimeError(format!("Zlib compression failed: {}", e)))
865 }
866
867 #[cfg(not(feature = "compression"))]
868 {
869 Ok(_data.to_vec())
871 }
872 }
873
874 fn compress_lz4(&self, data: &[u8]) -> QuantRS2Result<Vec<u8>> {
876 let mut compressed = Vec::new();
881 let mut i = 0;
882
883 while i < data.len() {
884 let byte = data[i];
885 let mut count = 1;
886
887 while i + count < data.len() && data[i + count] == byte && count < 255 {
888 count += 1;
889 }
890
891 if count > 3 {
892 compressed.push(0xFF); compressed.push(count as u8);
895 compressed.push(byte);
896 } else {
897 for _ in 0..count {
899 compressed.push(byte);
900 }
901 }
902
903 i += count;
904 }
905
906 Ok(compressed)
907 }
908
909 fn compress_quantum_optimized(
911 &self,
912 data: &[u8],
913 metadata: &GateMetadata,
914 ) -> QuantRS2Result<Vec<u8>> {
915 let mut compressed = Vec::new();
917
918 compressed.extend_from_slice(&(metadata.num_qubits as u32).to_le_bytes());
920 compressed.extend_from_slice(&metadata.sparsity_ratio.to_le_bytes());
921
922 if metadata.sparsity_ratio > 0.5 {
924 let complex_data = self.deserialize_matrix_from_bytes(data)?;
925 let _n = metadata.matrix_dims.0;
926
927 let mut non_zero_count = 0u32;
929 let mut non_zero_data = Vec::new();
930
931 for (idx, &complex) in complex_data.iter().enumerate() {
932 if complex.norm() >= self.config.tolerance {
933 non_zero_data.extend_from_slice(&(idx as u32).to_le_bytes());
934 non_zero_data.extend_from_slice(&complex.re.to_le_bytes());
935 non_zero_data.extend_from_slice(&complex.im.to_le_bytes());
936 non_zero_count += 1;
937 }
938 }
939
940 compressed.extend_from_slice(&non_zero_count.to_le_bytes());
941 compressed.extend_from_slice(&non_zero_data);
942 } else {
943 compressed.extend_from_slice(data);
945 }
946
947 Ok(compressed)
948 }
949
950 fn deserialize_matrix_from_bytes(&self, bytes: &[u8]) -> QuantRS2Result<Vec<Complex64>> {
952 if bytes.len() % 16 != 0 {
953 return Err(QuantRS2Error::InvalidInput(
954 "Invalid byte length for Complex64 array".to_string(),
955 ));
956 }
957
958 let mut matrix = Vec::with_capacity(bytes.len() / 16);
959
960 for chunk in bytes.chunks_exact(16) {
961 let re = f64::from_le_bytes([
962 chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
963 ]);
964 let im = f64::from_le_bytes([
965 chunk[8], chunk[9], chunk[10], chunk[11], chunk[12], chunk[13], chunk[14],
966 chunk[15],
967 ]);
968
969 matrix.push(Complex64::new(re, im));
970 }
971
972 Ok(matrix)
973 }
974
975 pub fn decompress_gate(&self, compressed: &CompressedGate) -> QuantRS2Result<Box<dyn GateOp>> {
977 match compressed {
978 CompressedGate::RuntimeCompressed {
979 compressed_data,
980 compression_type,
981 original_size,
982 gate_metadata,
983 } => {
984 let decompressed_bytes = self.decompress_data(
986 compressed_data,
987 *compression_type,
988 *original_size,
989 gate_metadata,
990 )?;
991
992 let matrix = self.deserialize_matrix_from_bytes(&decompressed_bytes)?;
994
995 let n = gate_metadata.matrix_dims.0;
997 let mut matrix_2d = Array2::zeros((n, n));
998 for j in 0..n {
999 for i in 0..n {
1000 matrix_2d[(i, j)] = matrix[j * n + i];
1001 }
1002 }
1003
1004 Ok(Box::new(CustomGate::with_qubits(
1005 gate_metadata.name.clone(),
1006 matrix_2d,
1007 gate_metadata.qubits.clone(),
1008 )))
1009 }
1010 CompressedGate::LowRank { left, right, .. } => {
1011 let reconstructed = left.dot(&right.t());
1013 Ok(Box::new(CustomGate::new(
1014 "LowRank".to_string(),
1015 reconstructed,
1016 )))
1017 }
1018 CompressedGate::Tucker { core, factors } => {
1019 let reconstructed = self.reconstruct_tucker(core, factors)?;
1021 Ok(Box::new(CustomGate::new(
1022 "Tucker".to_string(),
1023 reconstructed,
1024 )))
1025 }
1026 CompressedGate::Parameterized {
1027 gate_type,
1028 parameters,
1029 qubits,
1030 } => {
1031 self.reconstruct_parameterized_gate(gate_type, parameters, qubits)
1033 }
1034 CompressedGate::Original(gate) => Ok(gate.clone_gate()),
1035 }
1036 }
1037
1038 fn decompress_data(
1040 &self,
1041 compressed_data: &[u8],
1042 compression_type: CompressionType,
1043 original_size: usize,
1044 metadata: &GateMetadata,
1045 ) -> QuantRS2Result<Vec<u8>> {
1046 match compression_type {
1047 CompressionType::None => Ok(compressed_data.to_vec()),
1048 CompressionType::Zlib => self.decompress_zlib(compressed_data),
1049 CompressionType::LZ4 => self.decompress_lz4(compressed_data, original_size),
1050 CompressionType::QuantumOptimized => {
1051 self.decompress_quantum_optimized(compressed_data, metadata)
1052 }
1053 CompressionType::Huffman => {
1054 Ok(compressed_data.to_vec())
1056 }
1057 }
1058 }
1059
1060 fn decompress_zlib(&self, compressed_data: &[u8]) -> QuantRS2Result<Vec<u8>> {
1062 #[cfg(feature = "compression")]
1063 {
1064 use std::io::Read;
1065
1066 let mut decoder = flate2::read::ZlibDecoder::new(compressed_data);
1067 let mut decompressed = Vec::new();
1068
1069 decoder.read_to_end(&mut decompressed).map_err(|e| {
1070 QuantRS2Error::RuntimeError(format!("Zlib decompression failed: {}", e))
1071 })?;
1072
1073 Ok(decompressed)
1074 }
1075
1076 #[cfg(not(feature = "compression"))]
1077 {
1078 Ok(compressed_data.to_vec())
1080 }
1081 }
1082
1083 fn decompress_lz4(
1085 &self,
1086 compressed_data: &[u8],
1087 original_size: usize,
1088 ) -> QuantRS2Result<Vec<u8>> {
1089 let mut decompressed = Vec::with_capacity(original_size);
1091 let mut i = 0;
1092
1093 while i < compressed_data.len() {
1094 if compressed_data[i] == 0xFF && i + 2 < compressed_data.len() {
1095 let count = compressed_data[i + 1] as usize;
1097 let byte = compressed_data[i + 2];
1098
1099 for _ in 0..count {
1100 decompressed.push(byte);
1101 }
1102
1103 i += 3;
1104 } else {
1105 decompressed.push(compressed_data[i]);
1107 i += 1;
1108 }
1109 }
1110
1111 Ok(decompressed)
1112 }
1113
1114 fn decompress_quantum_optimized(
1116 &self,
1117 compressed_data: &[u8],
1118 metadata: &GateMetadata,
1119 ) -> QuantRS2Result<Vec<u8>> {
1120 if compressed_data.len() < 12 {
1121 return Err(QuantRS2Error::InvalidInput(
1122 "Invalid quantum-optimized compressed data".to_string(),
1123 ));
1124 }
1125
1126 let mut cursor = 0;
1127
1128 let _num_qubits = u32::from_le_bytes([
1130 compressed_data[cursor],
1131 compressed_data[cursor + 1],
1132 compressed_data[cursor + 2],
1133 compressed_data[cursor + 3],
1134 ]);
1135 cursor += 4;
1136
1137 let sparsity_ratio = f64::from_le_bytes([
1138 compressed_data[cursor],
1139 compressed_data[cursor + 1],
1140 compressed_data[cursor + 2],
1141 compressed_data[cursor + 3],
1142 compressed_data[cursor + 4],
1143 compressed_data[cursor + 5],
1144 compressed_data[cursor + 6],
1145 compressed_data[cursor + 7],
1146 ]);
1147 cursor += 8;
1148
1149 if sparsity_ratio > 0.5 {
1150 let non_zero_count = u32::from_le_bytes([
1152 compressed_data[cursor],
1153 compressed_data[cursor + 1],
1154 compressed_data[cursor + 2],
1155 compressed_data[cursor + 3],
1156 ]);
1157 cursor += 4;
1158
1159 let matrix_size = metadata.matrix_dims.0 * metadata.matrix_dims.1;
1160 let mut matrix = vec![Complex64::new(0.0, 0.0); matrix_size];
1161
1162 for _ in 0..non_zero_count {
1163 let index = u32::from_le_bytes([
1164 compressed_data[cursor],
1165 compressed_data[cursor + 1],
1166 compressed_data[cursor + 2],
1167 compressed_data[cursor + 3],
1168 ]) as usize;
1169 cursor += 4;
1170
1171 let re = f64::from_le_bytes([
1172 compressed_data[cursor],
1173 compressed_data[cursor + 1],
1174 compressed_data[cursor + 2],
1175 compressed_data[cursor + 3],
1176 compressed_data[cursor + 4],
1177 compressed_data[cursor + 5],
1178 compressed_data[cursor + 6],
1179 compressed_data[cursor + 7],
1180 ]);
1181 cursor += 8;
1182
1183 let im = f64::from_le_bytes([
1184 compressed_data[cursor],
1185 compressed_data[cursor + 1],
1186 compressed_data[cursor + 2],
1187 compressed_data[cursor + 3],
1188 compressed_data[cursor + 4],
1189 compressed_data[cursor + 5],
1190 compressed_data[cursor + 6],
1191 compressed_data[cursor + 7],
1192 ]);
1193 cursor += 8;
1194
1195 if index < matrix_size {
1196 matrix[index] = Complex64::new(re, im);
1197 }
1198 }
1199
1200 self.serialize_matrix(&matrix)
1202 } else {
1203 Ok(compressed_data[cursor..].to_vec())
1205 }
1206 }
1207
1208 fn reconstruct_parameterized_gate(
1210 &self,
1211 gate_type: &str,
1212 parameters: &[f64],
1213 qubits: &[QubitId],
1214 ) -> QuantRS2Result<Box<dyn GateOp>> {
1215 match gate_type {
1216 "rotation" => {
1217 if parameters.len() >= 3 && !qubits.is_empty() {
1218 let matrix = self.rotation_matrix_from_params(parameters, 2);
1220 Ok(Box::new(CustomGate::with_qubits(
1221 "Rotation".to_string(),
1222 matrix,
1223 qubits.to_vec(),
1224 )))
1225 } else {
1226 Err(QuantRS2Error::InvalidInput(
1227 "Invalid rotation parameters".to_string(),
1228 ))
1229 }
1230 }
1231 "phase" => {
1232 if !parameters.is_empty() && !qubits.is_empty() {
1233 let matrix = self.phase_matrix_from_params(parameters, 2);
1234 Ok(Box::new(CustomGate::with_qubits(
1235 "Phase".to_string(),
1236 matrix,
1237 qubits.to_vec(),
1238 )))
1239 } else {
1240 Err(QuantRS2Error::InvalidInput(
1241 "Invalid phase parameters".to_string(),
1242 ))
1243 }
1244 }
1245 _ => {
1246 let dim = 1 << qubits.len();
1248 let matrix = Array2::eye(dim);
1249 Ok(Box::new(CustomGate::with_qubits(
1250 gate_type.to_string(),
1251 matrix,
1252 qubits.to_vec(),
1253 )))
1254 }
1255 }
1256 }
1257}
1258
1259struct CompressionResult {
1261 data: Vec<u8>,
1262 compression_type: CompressionType,
1263 compression_ratio: f64,
1264}
1265
1266#[derive(Debug, Clone)]
1268pub struct CustomGate {
1269 name: String,
1270 matrix: Array2<Complex64>,
1271 qubits: Vec<QubitId>,
1272}
1273
1274impl CustomGate {
1275 pub fn new(name: String, matrix: Array2<Complex64>) -> Self {
1276 let n_qubits = (matrix.dim().0 as f64).log2() as usize;
1278 let qubits = (0..n_qubits).map(|i| QubitId::new(i as u32)).collect();
1279 Self {
1280 name,
1281 matrix,
1282 qubits,
1283 }
1284 }
1285
1286 pub fn with_qubits(name: String, matrix: Array2<Complex64>, qubits: Vec<QubitId>) -> Self {
1287 Self {
1288 name,
1289 matrix,
1290 qubits,
1291 }
1292 }
1293}
1294
1295impl GateOp for CustomGate {
1296 fn name(&self) -> &'static str {
1297 Box::leak(self.name.clone().into_boxed_str())
1299 }
1300
1301 fn qubits(&self) -> Vec<QubitId> {
1302 self.qubits.clone()
1303 }
1304
1305 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
1306 let mut result = Vec::with_capacity(self.matrix.len());
1308 let (rows, cols) = self.matrix.dim();
1309 for j in 0..cols {
1310 for i in 0..rows {
1311 result.push(self.matrix[(i, j)]);
1312 }
1313 }
1314 Ok(result)
1315 }
1316
1317 fn as_any(&self) -> &dyn Any {
1318 self
1319 }
1320
1321 fn clone_gate(&self) -> Box<dyn GateOp> {
1322 Box::new(self.clone())
1323 }
1324}
1325
1326#[derive(Debug, Clone, Default)]
1328pub struct CompressionStats {
1329 pub original_gates: usize,
1330 pub compressed_gates: usize,
1331 pub low_rank_compressions: usize,
1332 pub tucker_compressions: usize,
1333 pub parameterized_compressions: usize,
1334 pub compression_ratio: f64,
1335 pub total_parameters_before: usize,
1336 pub total_parameters_after: usize,
1337}
1338
1339impl GateSequenceCompressor {
1340 pub fn get_stats(
1342 &self,
1343 original: &[Box<dyn GateOp>],
1344 compressed: &[CompressedGate],
1345 ) -> CompressionStats {
1346 let mut stats = CompressionStats::default();
1347 stats.original_gates = original.len();
1348 stats.compressed_gates = compressed.len();
1349
1350 for gate in compressed {
1351 match gate {
1352 CompressedGate::LowRank { left, right, .. } => {
1353 stats.low_rank_compressions += 1;
1354 stats.total_parameters_after += (left.len() + right.len()) * 2;
1355 }
1356 CompressedGate::Tucker { core, factors } => {
1357 stats.tucker_compressions += 1;
1358 stats.total_parameters_after += core.len() * 2;
1359 stats.total_parameters_after +=
1360 factors.iter().map(|f| f.len() * 2).sum::<usize>();
1361 }
1362 CompressedGate::Parameterized { parameters, .. } => {
1363 stats.parameterized_compressions += 1;
1364 stats.total_parameters_after += parameters.len();
1365 }
1366 CompressedGate::RuntimeCompressed {
1367 compressed_data, ..
1368 } => {
1369 stats.total_parameters_after += compressed_data.len();
1371 }
1372 CompressedGate::Original(gate) => {
1373 if let Ok(matrix_vec) = gate.matrix() {
1374 let size = (matrix_vec.len() as f64).sqrt() as usize;
1375 stats.total_parameters_after += size * size * 2;
1376 }
1377 }
1378 }
1379 }
1380
1381 for gate in original {
1382 if let Ok(matrix_vec) = gate.matrix() {
1383 let size = (matrix_vec.len() as f64).sqrt() as usize;
1384 stats.total_parameters_before += size * size * 2;
1385 }
1386 }
1387
1388 stats.compression_ratio = if stats.total_parameters_before > 0 {
1389 stats.total_parameters_after as f64 / stats.total_parameters_before as f64
1390 } else {
1391 1.0
1392 };
1393
1394 stats
1395 }
1396}
1397
1398#[cfg(test)]
1399mod tests {
1400 use super::*;
1401 use crate::gate::single::{Hadamard, PauliX, PauliZ};
1402 use crate::qubit::QubitId;
1403
1404 #[test]
1405 fn test_gate_compression() {
1406 let config = CompressionConfig::default();
1407 let mut compressor = GateSequenceCompressor::new(config);
1408
1409 let h_gate = Hadamard {
1411 target: QubitId::new(0),
1412 };
1413 let compressed = compressor.compress_gate(&h_gate).unwrap();
1414
1415 match compressed {
1416 CompressedGate::Original(_) => {
1417 }
1419 CompressedGate::RuntimeCompressed { .. } => {
1420 }
1422 _ => panic!("H gate shouldn't be significantly compressed"),
1423 }
1424 }
1425
1426 #[test]
1427 fn test_sequence_compression() {
1428 let config = CompressionConfig::default();
1429 let mut compressor = GateSequenceCompressor::new(config);
1430
1431 let gates: Vec<Box<dyn GateOp>> = vec![
1433 Box::new(Hadamard {
1434 target: QubitId::new(0),
1435 }),
1436 Box::new(PauliX {
1437 target: QubitId::new(0),
1438 }),
1439 Box::new(Hadamard {
1440 target: QubitId::new(0),
1441 }),
1442 ];
1443
1444 let compressed = compressor.compress_sequence(&gates).unwrap();
1445 assert!(compressed.len() <= gates.len());
1446 }
1447
1448 #[test]
1449 fn test_compression_stats() {
1450 let config = CompressionConfig::default();
1451 let mut compressor = GateSequenceCompressor::new(config);
1452
1453 let gates: Vec<Box<dyn GateOp>> = vec![
1454 Box::new(Hadamard {
1455 target: QubitId::new(0),
1456 }),
1457 Box::new(PauliZ {
1458 target: QubitId::new(0),
1459 }),
1460 ];
1461
1462 let compressed = compressor.compress_sequence(&gates).unwrap();
1463 let stats = compressor.get_stats(&gates, &compressed);
1464
1465 assert_eq!(stats.original_gates, 2);
1466 assert!(stats.compression_ratio >= 0.0);
1468 }
1469}