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 objective = move |x: &scirs2_core::ndarray::ArrayView1<f64>| -> f64 {
412 let params: Vec<f64> = x.iter().copied().collect();
413
414 let gate_matrix = match gate_type_clone.as_str() {
416 "phase" => {
417 let mut matrix = Array2::eye(target_matrix_clone.dim().0);
418 if !params.is_empty() {
419 let phase = Complex64::from_polar(1.0, params[0]);
420 let n = matrix.dim().0;
421 matrix[(n - 1, n - 1)] = phase;
422 }
423 matrix
424 }
425 "rotation" | _ => Array2::eye(target_matrix_clone.dim().0), };
427
428 let diff = &target_matrix_clone - &gate_matrix;
430 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
431 };
432
433 let mut options = DifferentialEvolutionOptions::default();
435 options.popsize = 50;
436 options.maxiter = self.config.max_iterations;
437 options.tol = self.config.tolerance;
438
439 let de_bounds: Vec<(f64, f64)> = bounds
440 .into_iter()
441 .map(|(low, high)| {
442 (
443 low.unwrap_or(-std::f64::consts::PI),
444 high.unwrap_or(std::f64::consts::PI),
445 )
446 })
447 .collect();
448
449 let result =
450 differential_evolution(objective, &de_bounds, Some(options), None).map_err(|e| {
451 QuantRS2Error::InvalidInput(format!("Parameter optimization failed: {e:?}"))
452 })?;
453
454 if result.fun > self.config.tolerance {
455 return Ok(None);
457 }
458
459 Ok(Some(CompressedGate::Parameterized {
460 gate_type,
461 parameters: result.x.to_vec(),
462 qubits: vec![], }))
464 }
465
466 fn merge_adjacent_gates(
468 &self,
469 gates: &[Box<dyn GateOp>],
470 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
471 let mut merged = Vec::new();
472 let mut i = 0;
473
474 while i < gates.len() {
475 if i + 1 < gates.len() {
476 if self.can_merge(gates[i].as_ref(), gates[i + 1].as_ref()) {
478 let combined =
480 self.merge_two_gates(gates[i].as_ref(), gates[i + 1].as_ref())?;
481 merged.push(combined);
482 i += 2;
483 } else {
484 merged.push(gates[i].clone_gate());
485 i += 1;
486 }
487 } else {
488 merged.push(gates[i].clone_gate());
489 i += 1;
490 }
491 }
492
493 Ok(merged)
494 }
495
496 fn can_merge(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
498 gate1.name() == gate2.name()
505 }
506
507 fn merge_two_gates(
509 &self,
510 gate1: &dyn GateOp,
511 gate2: &dyn GateOp,
512 ) -> QuantRS2Result<Box<dyn GateOp>> {
513 let matrix1_vec = gate1.matrix()?;
515 let matrix2_vec = gate2.matrix()?;
516
517 let n = (matrix1_vec.len() as f64).sqrt() as usize;
519 let mut matrix1 = Array2::zeros((n, n));
520 let mut matrix2 = Array2::zeros((n, n));
521
522 for j in 0..n {
523 for i in 0..n {
524 matrix1[(i, j)] = matrix1_vec[j * n + i];
525 matrix2[(i, j)] = matrix2_vec[j * n + i];
526 }
527 }
528
529 let combined_matrix = matrix2.dot(&matrix1);
531
532 Ok(Box::new(CustomGate::new(
534 format!("{}_{}_merged", gate1.name(), gate2.name()),
535 combined_matrix,
536 )))
537 }
538
539 fn compute_matrix_hash(&self, matrix: &ArrayView2<Complex64>) -> u64 {
541 use std::collections::hash_map::DefaultHasher;
542 use std::hash::{Hash, Hasher};
543
544 let mut hasher = DefaultHasher::new();
545 for elem in matrix {
546 elem.re.to_bits().hash(&mut hasher);
548 elem.im.to_bits().hash(&mut hasher);
549 }
550 hasher.finish()
551 }
552
553 fn find_effective_rank(
555 &self,
556 s_real: &scirs2_core::ndarray::Array1<f64>,
557 s_imag: &scirs2_core::ndarray::Array1<f64>,
558 ) -> QuantRS2Result<usize> {
559 let max_singular = s_real
560 .iter()
561 .chain(s_imag.iter())
562 .map(|s| s.abs())
563 .fold(0.0, f64::max);
564
565 let threshold = max_singular * self.config.tolerance;
566
567 let rank = s_real
568 .iter()
569 .zip(s_imag.iter())
570 .take_while(|(sr, si)| sr.abs() > threshold || si.abs() > threshold)
571 .count();
572
573 Ok(rank.max(1))
574 }
575
576 fn combine_complex(
578 &self,
579 real: &Array2<f64>,
580 imag: &Array2<f64>,
581 rank: usize,
582 ) -> QuantRS2Result<Array2<Complex64>> {
583 let (rows, _) = real.dim();
584 let result = Array2::from_shape_fn((rows, rank), |(i, j)| {
585 Complex64::new(real[(i, j)], imag[(i, j)])
586 });
587 Ok(result)
588 }
589
590 fn combine_complex_with_singular(
592 &self,
593 vt_real: &Array2<f64>,
594 vt_imag: &Array2<f64>,
595 s_real: &scirs2_core::ndarray::Array1<f64>,
596 s_imag: &scirs2_core::ndarray::Array1<f64>,
597 rank: usize,
598 ) -> QuantRS2Result<Array2<Complex64>> {
599 let (_, cols) = vt_real.dim();
600 let result = Array2::from_shape_fn((rank, cols), |(i, j)| {
601 let s = Complex64::new(s_real[i], s_imag[i]);
602 let v = Complex64::new(vt_real[(i, j)], vt_imag[(i, j)]);
603 s * v
604 });
605 Ok(result)
606 }
607
608 #[allow(dead_code)]
610 fn tensor_to_complex_matrix(&self, tensor: &[f64]) -> QuantRS2Result<Array2<Complex64>> {
611 let size = (tensor.len() / 2) as f64;
612 let dim = size.sqrt() as usize;
613
614 let mut matrix = Array2::zeros((dim, dim));
615 for i in 0..dim {
616 for j in 0..dim {
617 let idx = (i * dim + j) * 2;
618 matrix[(i, j)] = Complex64::new(tensor[idx], tensor[idx + 1]);
619 }
620 }
621
622 Ok(matrix)
623 }
624
625 #[allow(dead_code)]
627 fn tensor_to_complex_matrix_from_array(
628 &self,
629 tensor: &scirs2_core::ndarray::ArrayD<f64>,
630 ) -> QuantRS2Result<Array2<Complex64>> {
631 let elements: Vec<f64> = tensor.iter().copied().collect();
633 let size = elements.len() as f64;
634 let dim = size.sqrt() as usize;
635
636 if dim * dim == elements.len() {
637 let mut matrix = Array2::zeros((dim, dim));
638 for i in 0..dim {
639 for j in 0..dim {
640 let idx = i * dim + j;
641 matrix[(i, j)] = Complex64::new(elements[idx], 0.0);
642 }
643 }
644 Ok(matrix)
645 } else {
646 let dim = (size.sqrt().ceil()) as usize;
648 let mut matrix = Array2::zeros((dim, dim));
649 for (idx, &val) in elements.iter().enumerate() {
650 let i = idx / dim;
651 let j = idx % dim;
652 if i < dim && j < dim {
653 matrix[(i, j)] = Complex64::new(val, 0.0);
654 }
655 }
656 Ok(matrix)
657 }
658 }
659
660 fn identify_gate_type(&self, gate: &dyn GateOp) -> String {
662 let name = gate.name();
664 if name.contains("rot") || name.contains("Rot") {
665 "rotation".to_string()
666 } else if name.contains("phase") || name.contains("Phase") {
667 "phase".to_string()
668 } else {
669 "general".to_string()
670 }
671 }
672
673 #[allow(dead_code)]
675 fn evaluate_gate_parameters(
676 &self,
677 target: &Array2<Complex64>,
678 gate_type: &str,
679 params: &[f64],
680 ) -> f64 {
681 let gate_matrix = match gate_type {
683 "rotation" => self.rotation_matrix_from_params(params, target.dim().0),
684 "phase" => self.phase_matrix_from_params(params, target.dim().0),
685 _ => self.general_matrix_from_params(params, target.dim().0),
686 };
687
688 let diff = target - &gate_matrix;
690 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
691 }
692
693 fn rotation_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
694 Array2::eye(dim)
697 }
698
699 fn phase_matrix_from_params(&self, params: &[f64], dim: usize) -> Array2<Complex64> {
700 let mut matrix = Array2::eye(dim);
701 if !params.is_empty() {
702 let phase = Complex64::from_polar(1.0, params[0]);
703 matrix[(dim - 1, dim - 1)] = phase;
704 }
705 matrix
706 }
707
708 #[allow(dead_code)]
709 fn general_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
710 Array2::eye(dim)
712 }
713
714 fn try_runtime_compression(&self, gate: &dyn GateOp) -> QuantRS2Result<Option<CompressedGate>> {
716 let matrix_vec = gate.matrix()?;
717 let n = (matrix_vec.len() as f64).sqrt() as usize;
718
719 let metadata = GateMetadata {
721 name: gate.name().to_string(),
722 num_qubits: gate.num_qubits(),
723 qubits: gate.qubits(),
724 matrix_dims: (n, n),
725 sparsity_ratio: self.calculate_sparsity_ratio(&matrix_vec),
726 is_unitary: self.check_unitary(&matrix_vec, n),
727 };
728
729 let matrix_bytes = self.serialize_matrix(&matrix_vec)?;
731
732 let best_compression = self.find_best_compression(&matrix_bytes, &metadata)?;
734
735 if best_compression.compression_ratio < 0.8 {
737 Ok(Some(CompressedGate::RuntimeCompressed {
738 compressed_data: best_compression.data,
739 compression_type: best_compression.compression_type,
740 original_size: matrix_bytes.len(),
741 gate_metadata: metadata,
742 }))
743 } else {
744 Ok(None)
745 }
746 }
747
748 fn calculate_sparsity_ratio(&self, matrix: &[Complex64]) -> f64 {
750 let zero_count = matrix
751 .iter()
752 .filter(|&c| c.norm() < self.config.tolerance)
753 .count();
754 zero_count as f64 / matrix.len() as f64
755 }
756
757 fn check_unitary(&self, matrix: &[Complex64], n: usize) -> bool {
759 if n > 8 {
762 return false; }
764
765 let mut u = Array2::zeros((n, n));
767 for j in 0..n {
768 for i in 0..n {
769 u[(i, j)] = matrix[j * n + i];
770 }
771 }
772
773 let u_dagger = u.t().mapv(|c| c.conj());
775 let product = u_dagger.dot(&u);
776
777 let identity = Array2::<Complex64>::eye(n);
779 let diff = &product - &identity;
780 let frobenius_norm: f64 = diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
781
782 frobenius_norm < self.config.tolerance
783 }
784
785 fn serialize_matrix(&self, matrix: &[Complex64]) -> QuantRS2Result<Vec<u8>> {
787 let mut bytes = Vec::with_capacity(matrix.len() * 16); for &complex in matrix {
790 bytes.extend_from_slice(&complex.re.to_le_bytes());
791 bytes.extend_from_slice(&complex.im.to_le_bytes());
792 }
793
794 Ok(bytes)
795 }
796
797 fn find_best_compression(
799 &self,
800 data: &[u8],
801 metadata: &GateMetadata,
802 ) -> QuantRS2Result<CompressionResult> {
803 let mut best_compression = CompressionResult {
804 data: data.to_vec(),
805 compression_type: CompressionType::None,
806 compression_ratio: 1.0,
807 };
808
809 if let Ok(zlib_compressed) = self.compress_zlib(data) {
811 let ratio = zlib_compressed.len() as f64 / data.len() as f64;
812 if ratio < best_compression.compression_ratio {
813 best_compression = CompressionResult {
814 data: zlib_compressed,
815 compression_type: CompressionType::Zlib,
816 compression_ratio: ratio,
817 };
818 }
819 }
820
821 if let Ok(lz4_compressed) = self.compress_lz4(data) {
823 let ratio = lz4_compressed.len() as f64 / data.len() as f64;
824 if ratio < best_compression.compression_ratio {
825 best_compression = CompressionResult {
826 data: lz4_compressed,
827 compression_type: CompressionType::LZ4,
828 compression_ratio: ratio,
829 };
830 }
831 }
832
833 if metadata.sparsity_ratio > 0.3 {
835 if let Ok(quantum_compressed) = self.compress_quantum_optimized(data, metadata) {
836 let ratio = quantum_compressed.len() as f64 / data.len() as f64;
837 if ratio < best_compression.compression_ratio {
838 best_compression = CompressionResult {
839 data: quantum_compressed,
840 compression_type: CompressionType::QuantumOptimized,
841 compression_ratio: ratio,
842 };
843 }
844 }
845 }
846
847 Ok(best_compression)
848 }
849
850 fn compress_zlib(&self, data: &[u8]) -> QuantRS2Result<Vec<u8>> {
852 #[cfg(feature = "compression")]
853 {
854 use std::io::Write;
855 let mut encoder =
856 flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
857 encoder.write_all(data).map_err(|e| {
858 QuantRS2Error::RuntimeError(format!("Zlib compression failed: {e}"))
859 })?;
860
861 encoder
862 .finish()
863 .map_err(|e| QuantRS2Error::RuntimeError(format!("Zlib compression failed: {e}")))
864 }
865
866 #[cfg(not(feature = "compression"))]
867 {
868 Ok(data.to_vec())
870 }
871 }
872
873 fn compress_lz4(&self, data: &[u8]) -> QuantRS2Result<Vec<u8>> {
875 let mut compressed = Vec::new();
880 let mut i = 0;
881
882 while i < data.len() {
883 let byte = data[i];
884 let mut count = 1;
885
886 while i + count < data.len() && data[i + count] == byte && count < 255 {
887 count += 1;
888 }
889
890 if count > 3 {
891 compressed.push(0xFF); compressed.push(count as u8);
894 compressed.push(byte);
895 } else {
896 for _ in 0..count {
898 compressed.push(byte);
899 }
900 }
901
902 i += count;
903 }
904
905 Ok(compressed)
906 }
907
908 fn compress_quantum_optimized(
910 &self,
911 data: &[u8],
912 metadata: &GateMetadata,
913 ) -> QuantRS2Result<Vec<u8>> {
914 let mut compressed = Vec::new();
916
917 compressed.extend_from_slice(&(metadata.num_qubits as u32).to_le_bytes());
919 compressed.extend_from_slice(&metadata.sparsity_ratio.to_le_bytes());
920
921 if metadata.sparsity_ratio > 0.5 {
923 let complex_data = self.deserialize_matrix_from_bytes(data)?;
924 let mut non_zero_count = 0u32;
928 let mut non_zero_data = Vec::new();
929
930 for (idx, &complex) in complex_data.iter().enumerate() {
931 if complex.norm() >= self.config.tolerance {
932 non_zero_data.extend_from_slice(&(idx as u32).to_le_bytes());
933 non_zero_data.extend_from_slice(&complex.re.to_le_bytes());
934 non_zero_data.extend_from_slice(&complex.im.to_le_bytes());
935 non_zero_count += 1;
936 }
937 }
938
939 compressed.extend_from_slice(&non_zero_count.to_le_bytes());
940 compressed.extend_from_slice(&non_zero_data);
941 } else {
942 compressed.extend_from_slice(data);
944 }
945
946 Ok(compressed)
947 }
948
949 fn deserialize_matrix_from_bytes(&self, bytes: &[u8]) -> QuantRS2Result<Vec<Complex64>> {
951 if bytes.len() % 16 != 0 {
952 return Err(QuantRS2Error::InvalidInput(
953 "Invalid byte length for Complex64 array".to_string(),
954 ));
955 }
956
957 let mut matrix = Vec::with_capacity(bytes.len() / 16);
958
959 for chunk in bytes.chunks_exact(16) {
960 let re = f64::from_le_bytes([
961 chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
962 ]);
963 let im = f64::from_le_bytes([
964 chunk[8], chunk[9], chunk[10], chunk[11], chunk[12], chunk[13], chunk[14],
965 chunk[15],
966 ]);
967
968 matrix.push(Complex64::new(re, im));
969 }
970
971 Ok(matrix)
972 }
973
974 pub fn decompress_gate(&self, compressed: &CompressedGate) -> QuantRS2Result<Box<dyn GateOp>> {
976 match compressed {
977 CompressedGate::RuntimeCompressed {
978 compressed_data,
979 compression_type,
980 original_size,
981 gate_metadata,
982 } => {
983 let decompressed_bytes = self.decompress_data(
985 compressed_data,
986 *compression_type,
987 *original_size,
988 gate_metadata,
989 )?;
990
991 let matrix = self.deserialize_matrix_from_bytes(&decompressed_bytes)?;
993
994 let n = gate_metadata.matrix_dims.0;
996 let mut matrix_2d = Array2::zeros((n, n));
997 for j in 0..n {
998 for i in 0..n {
999 matrix_2d[(i, j)] = matrix[j * n + i];
1000 }
1001 }
1002
1003 Ok(Box::new(CustomGate::with_qubits(
1004 gate_metadata.name.clone(),
1005 matrix_2d,
1006 gate_metadata.qubits.clone(),
1007 )))
1008 }
1009 CompressedGate::LowRank { left, right, .. } => {
1010 let reconstructed = left.dot(&right.t());
1012 Ok(Box::new(CustomGate::new(
1013 "LowRank".to_string(),
1014 reconstructed,
1015 )))
1016 }
1017 CompressedGate::Tucker { core, factors } => {
1018 let reconstructed = self.reconstruct_tucker(core, factors)?;
1020 Ok(Box::new(CustomGate::new(
1021 "Tucker".to_string(),
1022 reconstructed,
1023 )))
1024 }
1025 CompressedGate::Parameterized {
1026 gate_type,
1027 parameters,
1028 qubits,
1029 } => {
1030 self.reconstruct_parameterized_gate(gate_type, parameters, qubits)
1032 }
1033 CompressedGate::Original(gate) => Ok(gate.clone_gate()),
1034 }
1035 }
1036
1037 fn decompress_data(
1039 &self,
1040 compressed_data: &[u8],
1041 compression_type: CompressionType,
1042 original_size: usize,
1043 metadata: &GateMetadata,
1044 ) -> QuantRS2Result<Vec<u8>> {
1045 match compression_type {
1046 CompressionType::None => Ok(compressed_data.to_vec()),
1047 CompressionType::Zlib => self.decompress_zlib(compressed_data),
1048 CompressionType::LZ4 => self.decompress_lz4(compressed_data, original_size),
1049 CompressionType::QuantumOptimized => {
1050 self.decompress_quantum_optimized(compressed_data, metadata)
1051 }
1052 CompressionType::Huffman => {
1053 Ok(compressed_data.to_vec())
1055 }
1056 }
1057 }
1058
1059 fn decompress_zlib(&self, compressed_data: &[u8]) -> QuantRS2Result<Vec<u8>> {
1061 #[cfg(feature = "compression")]
1062 {
1063 use std::io::Read;
1064
1065 let mut decoder = flate2::read::ZlibDecoder::new(compressed_data);
1066 let mut decompressed = Vec::new();
1067
1068 decoder.read_to_end(&mut decompressed).map_err(|e| {
1069 QuantRS2Error::RuntimeError(format!("Zlib decompression failed: {e}"))
1070 })?;
1071
1072 Ok(decompressed)
1073 }
1074
1075 #[cfg(not(feature = "compression"))]
1076 {
1077 Ok(compressed_data.to_vec())
1079 }
1080 }
1081
1082 fn decompress_lz4(
1084 &self,
1085 compressed_data: &[u8],
1086 original_size: usize,
1087 ) -> QuantRS2Result<Vec<u8>> {
1088 let mut decompressed = Vec::with_capacity(original_size);
1090 let mut i = 0;
1091
1092 while i < compressed_data.len() {
1093 if compressed_data[i] == 0xFF && i + 2 < compressed_data.len() {
1094 let count = compressed_data[i + 1] as usize;
1096 let byte = compressed_data[i + 2];
1097
1098 for _ in 0..count {
1099 decompressed.push(byte);
1100 }
1101
1102 i += 3;
1103 } else {
1104 decompressed.push(compressed_data[i]);
1106 i += 1;
1107 }
1108 }
1109
1110 Ok(decompressed)
1111 }
1112
1113 fn decompress_quantum_optimized(
1115 &self,
1116 compressed_data: &[u8],
1117 metadata: &GateMetadata,
1118 ) -> QuantRS2Result<Vec<u8>> {
1119 if compressed_data.len() < 12 {
1120 return Err(QuantRS2Error::InvalidInput(
1121 "Invalid quantum-optimized compressed data".to_string(),
1122 ));
1123 }
1124
1125 let mut cursor = 0;
1126
1127 let _num_qubits = u32::from_le_bytes([
1129 compressed_data[cursor],
1130 compressed_data[cursor + 1],
1131 compressed_data[cursor + 2],
1132 compressed_data[cursor + 3],
1133 ]);
1134 cursor += 4;
1135
1136 let sparsity_ratio = f64::from_le_bytes([
1137 compressed_data[cursor],
1138 compressed_data[cursor + 1],
1139 compressed_data[cursor + 2],
1140 compressed_data[cursor + 3],
1141 compressed_data[cursor + 4],
1142 compressed_data[cursor + 5],
1143 compressed_data[cursor + 6],
1144 compressed_data[cursor + 7],
1145 ]);
1146 cursor += 8;
1147
1148 if sparsity_ratio > 0.5 {
1149 let non_zero_count = u32::from_le_bytes([
1151 compressed_data[cursor],
1152 compressed_data[cursor + 1],
1153 compressed_data[cursor + 2],
1154 compressed_data[cursor + 3],
1155 ]);
1156 cursor += 4;
1157
1158 let matrix_size = metadata.matrix_dims.0 * metadata.matrix_dims.1;
1159 let mut matrix = vec![Complex64::new(0.0, 0.0); matrix_size];
1160
1161 for _ in 0..non_zero_count {
1162 let index = u32::from_le_bytes([
1163 compressed_data[cursor],
1164 compressed_data[cursor + 1],
1165 compressed_data[cursor + 2],
1166 compressed_data[cursor + 3],
1167 ]) as usize;
1168 cursor += 4;
1169
1170 let re = f64::from_le_bytes([
1171 compressed_data[cursor],
1172 compressed_data[cursor + 1],
1173 compressed_data[cursor + 2],
1174 compressed_data[cursor + 3],
1175 compressed_data[cursor + 4],
1176 compressed_data[cursor + 5],
1177 compressed_data[cursor + 6],
1178 compressed_data[cursor + 7],
1179 ]);
1180 cursor += 8;
1181
1182 let im = f64::from_le_bytes([
1183 compressed_data[cursor],
1184 compressed_data[cursor + 1],
1185 compressed_data[cursor + 2],
1186 compressed_data[cursor + 3],
1187 compressed_data[cursor + 4],
1188 compressed_data[cursor + 5],
1189 compressed_data[cursor + 6],
1190 compressed_data[cursor + 7],
1191 ]);
1192 cursor += 8;
1193
1194 if index < matrix_size {
1195 matrix[index] = Complex64::new(re, im);
1196 }
1197 }
1198
1199 self.serialize_matrix(&matrix)
1201 } else {
1202 Ok(compressed_data[cursor..].to_vec())
1204 }
1205 }
1206
1207 fn reconstruct_parameterized_gate(
1209 &self,
1210 gate_type: &str,
1211 parameters: &[f64],
1212 qubits: &[QubitId],
1213 ) -> QuantRS2Result<Box<dyn GateOp>> {
1214 match gate_type {
1215 "rotation" => {
1216 if parameters.len() >= 3 && !qubits.is_empty() {
1217 let matrix = self.rotation_matrix_from_params(parameters, 2);
1219 Ok(Box::new(CustomGate::with_qubits(
1220 "Rotation".to_string(),
1221 matrix,
1222 qubits.to_vec(),
1223 )))
1224 } else {
1225 Err(QuantRS2Error::InvalidInput(
1226 "Invalid rotation parameters".to_string(),
1227 ))
1228 }
1229 }
1230 "phase" => {
1231 if !parameters.is_empty() && !qubits.is_empty() {
1232 let matrix = self.phase_matrix_from_params(parameters, 2);
1233 Ok(Box::new(CustomGate::with_qubits(
1234 "Phase".to_string(),
1235 matrix,
1236 qubits.to_vec(),
1237 )))
1238 } else {
1239 Err(QuantRS2Error::InvalidInput(
1240 "Invalid phase parameters".to_string(),
1241 ))
1242 }
1243 }
1244 _ => {
1245 let dim = 1 << qubits.len();
1247 let matrix = Array2::eye(dim);
1248 Ok(Box::new(CustomGate::with_qubits(
1249 gate_type.to_string(),
1250 matrix,
1251 qubits.to_vec(),
1252 )))
1253 }
1254 }
1255 }
1256}
1257
1258struct CompressionResult {
1260 data: Vec<u8>,
1261 compression_type: CompressionType,
1262 compression_ratio: f64,
1263}
1264
1265#[derive(Debug, Clone)]
1267pub struct CustomGate {
1268 name: String,
1269 matrix: Array2<Complex64>,
1270 qubits: Vec<QubitId>,
1271}
1272
1273impl CustomGate {
1274 pub fn new(name: String, matrix: Array2<Complex64>) -> Self {
1275 let n_qubits = (matrix.dim().0 as f64).log2() as usize;
1277 let qubits = (0..n_qubits).map(|i| QubitId::new(i as u32)).collect();
1278 Self {
1279 name,
1280 matrix,
1281 qubits,
1282 }
1283 }
1284
1285 pub const fn with_qubits(
1286 name: String,
1287 matrix: Array2<Complex64>,
1288 qubits: Vec<QubitId>,
1289 ) -> Self {
1290 Self {
1291 name,
1292 matrix,
1293 qubits,
1294 }
1295 }
1296}
1297
1298impl GateOp for CustomGate {
1299 fn name(&self) -> &'static str {
1300 Box::leak(self.name.clone().into_boxed_str())
1302 }
1303
1304 fn qubits(&self) -> Vec<QubitId> {
1305 self.qubits.clone()
1306 }
1307
1308 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
1309 let mut result = Vec::with_capacity(self.matrix.len());
1311 let (rows, cols) = self.matrix.dim();
1312 for j in 0..cols {
1313 for i in 0..rows {
1314 result.push(self.matrix[(i, j)]);
1315 }
1316 }
1317 Ok(result)
1318 }
1319
1320 fn as_any(&self) -> &dyn Any {
1321 self
1322 }
1323
1324 fn clone_gate(&self) -> Box<dyn GateOp> {
1325 Box::new(self.clone())
1326 }
1327}
1328
1329#[derive(Debug, Clone, Default)]
1331pub struct CompressionStats {
1332 pub original_gates: usize,
1333 pub compressed_gates: usize,
1334 pub low_rank_compressions: usize,
1335 pub tucker_compressions: usize,
1336 pub parameterized_compressions: usize,
1337 pub compression_ratio: f64,
1338 pub total_parameters_before: usize,
1339 pub total_parameters_after: usize,
1340}
1341
1342impl GateSequenceCompressor {
1343 pub fn get_stats(
1345 &self,
1346 original: &[Box<dyn GateOp>],
1347 compressed: &[CompressedGate],
1348 ) -> CompressionStats {
1349 let mut stats = CompressionStats::default();
1350 stats.original_gates = original.len();
1351 stats.compressed_gates = compressed.len();
1352
1353 for gate in compressed {
1354 match gate {
1355 CompressedGate::LowRank { left, right, .. } => {
1356 stats.low_rank_compressions += 1;
1357 stats.total_parameters_after += (left.len() + right.len()) * 2;
1358 }
1359 CompressedGate::Tucker { core, factors } => {
1360 stats.tucker_compressions += 1;
1361 stats.total_parameters_after += core.len() * 2;
1362 stats.total_parameters_after +=
1363 factors.iter().map(|f| f.len() * 2).sum::<usize>();
1364 }
1365 CompressedGate::Parameterized { parameters, .. } => {
1366 stats.parameterized_compressions += 1;
1367 stats.total_parameters_after += parameters.len();
1368 }
1369 CompressedGate::RuntimeCompressed {
1370 compressed_data, ..
1371 } => {
1372 stats.total_parameters_after += compressed_data.len();
1374 }
1375 CompressedGate::Original(gate) => {
1376 if let Ok(matrix_vec) = gate.matrix() {
1377 let size = (matrix_vec.len() as f64).sqrt() as usize;
1378 stats.total_parameters_after += size * size * 2;
1379 }
1380 }
1381 }
1382 }
1383
1384 for gate in original {
1385 if let Ok(matrix_vec) = gate.matrix() {
1386 let size = (matrix_vec.len() as f64).sqrt() as usize;
1387 stats.total_parameters_before += size * size * 2;
1388 }
1389 }
1390
1391 stats.compression_ratio = if stats.total_parameters_before > 0 {
1392 stats.total_parameters_after as f64 / stats.total_parameters_before as f64
1393 } else {
1394 1.0
1395 };
1396
1397 stats
1398 }
1399}
1400
1401#[cfg(test)]
1402mod tests {
1403 use super::*;
1404 use crate::gate::single::{Hadamard, PauliX, PauliZ};
1405 use crate::qubit::QubitId;
1406
1407 #[test]
1408 fn test_gate_compression() {
1409 let config = CompressionConfig::default();
1410 let mut compressor = GateSequenceCompressor::new(config);
1411
1412 let h_gate = Hadamard {
1414 target: QubitId::new(0),
1415 };
1416 let compressed = compressor
1417 .compress_gate(&h_gate)
1418 .expect("Failed to compress Hadamard gate");
1419
1420 match compressed {
1421 CompressedGate::Original(_) => {
1422 }
1424 CompressedGate::RuntimeCompressed { .. } => {
1425 }
1427 _ => panic!("H gate shouldn't be significantly compressed"),
1428 }
1429 }
1430
1431 #[test]
1432 fn test_sequence_compression() {
1433 let config = CompressionConfig::default();
1434 let mut compressor = GateSequenceCompressor::new(config);
1435
1436 let gates: Vec<Box<dyn GateOp>> = vec![
1438 Box::new(Hadamard {
1439 target: QubitId::new(0),
1440 }),
1441 Box::new(PauliX {
1442 target: QubitId::new(0),
1443 }),
1444 Box::new(Hadamard {
1445 target: QubitId::new(0),
1446 }),
1447 ];
1448
1449 let compressed = compressor
1450 .compress_sequence(&gates)
1451 .expect("Failed to compress gate sequence");
1452 assert!(compressed.len() <= gates.len());
1453 }
1454
1455 #[test]
1456 fn test_compression_stats() {
1457 let config = CompressionConfig::default();
1458 let mut compressor = GateSequenceCompressor::new(config);
1459
1460 let gates: Vec<Box<dyn GateOp>> = vec![
1461 Box::new(Hadamard {
1462 target: QubitId::new(0),
1463 }),
1464 Box::new(PauliZ {
1465 target: QubitId::new(0),
1466 }),
1467 ];
1468
1469 let compressed = compressor
1470 .compress_sequence(&gates)
1471 .expect("Failed to compress gate sequence for stats");
1472 let stats = compressor.get_stats(&gates, &compressed);
1473
1474 assert_eq!(stats.original_gates, 2);
1475 assert!(stats.compression_ratio >= 0.0);
1477 }
1478}