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 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 rayon::ThreadPoolBuilder::new()
178 .num_threads(threads)
179 .build_global()
180 .map_err(|e| QuantRS2Error::InvalidInput(e.to_string()))?;
181 }
182
183 let merged = self.merge_adjacent_gates(gates)?;
185
186 let compressed: Result<Vec<_>, _> = merged
188 .iter()
189 .map(|gate| self.compress_gate(gate.as_ref()))
190 .collect();
191
192 compressed
193 }
194
195 fn try_low_rank_approximation(
197 &self,
198 matrix: &ArrayView2<Complex64>,
199 ) -> QuantRS2Result<Option<CompressedGate>> {
200 let (rows, cols) = matrix.dim();
201 if rows != cols || rows < 4 {
202 return Ok(None);
204 }
205
206 let real_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].re);
208 let imag_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].im);
209
210 let target_rank = self.config.max_rank.unwrap_or(rows / 2);
212
213 let (u_real, s_real, vt_real) = if self.config.use_randomized {
215 randomized_svd(&real_part.view(), target_rank, Some(10), Some(2), None)
216 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
217 } else {
218 truncated_svd(&real_part.view(), target_rank, None)
219 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
220 };
221
222 let (u_imag, s_imag, vt_imag) = if self.config.use_randomized {
223 randomized_svd(&imag_part.view(), target_rank, Some(10), Some(2), None)
224 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
225 } else {
226 truncated_svd(&imag_part.view(), target_rank, None)
227 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
228 };
229
230 let effective_rank = self.find_effective_rank(&s_real, &s_imag)?;
232
233 if effective_rank >= rows * 3 / 4 {
234 return Ok(None);
236 }
237
238 let left = self.combine_complex(&u_real, &u_imag, effective_rank)?;
240 let right = self.combine_complex_with_singular(
241 &vt_real,
242 &vt_imag,
243 &s_real,
244 &s_imag,
245 effective_rank,
246 )?;
247
248 let approx = left.dot(&right.t());
250 if !matrices_approx_equal(&approx.view(), matrix, self.config.tolerance) {
251 return Ok(None);
252 }
253
254 Ok(Some(CompressedGate::LowRank {
255 left,
256 right,
257 rank: effective_rank,
258 }))
259 }
260
261 fn try_tucker_decomposition(
263 &self,
264 _matrix: &ArrayView2<Complex64>,
265 ) -> QuantRS2Result<Option<CompressedGate>> {
266 Ok(None)
269 }
270
271 fn try_parameterized_compression(
273 &self,
274 gate: &dyn GateOp,
275 ) -> QuantRS2Result<Option<CompressedGate>> {
276 let matrix_vec = gate.matrix()?;
281 let n = (matrix_vec.len() as f64).sqrt() as usize;
282
283 if n > 4 {
284 return Ok(None);
286 }
287
288 let mut target_matrix = Array2::zeros((n, n));
290 for j in 0..n {
291 for i in 0..n {
292 target_matrix[(i, j)] = matrix_vec[j * n + i];
293 }
294 }
295
296 let gate_type = self.identify_gate_type(gate);
297
298 let dim = match gate_type.as_str() {
300 "rotation" => 3, "phase" => 1, _ => 6, };
304 let bounds = vec![(Some(-std::f64::consts::PI), Some(std::f64::consts::PI)); dim];
305
306 let target_matrix_clone = target_matrix.clone();
308 let gate_type_clone = gate_type.clone();
309 let _tolerance = self.config.tolerance;
310
311 let objective = move |x: &scirs2_core::ndarray::ArrayView1<f64>| -> f64 {
313 let params: Vec<f64> = x.iter().cloned().collect();
314
315 let gate_matrix = match gate_type_clone.as_str() {
317 "rotation" => Array2::eye(target_matrix_clone.dim().0), "phase" => {
319 let mut matrix = Array2::eye(target_matrix_clone.dim().0);
320 if !params.is_empty() {
321 let phase = Complex64::from_polar(1.0, params[0]);
322 let n = matrix.dim().0;
323 matrix[(n - 1, n - 1)] = phase;
324 }
325 matrix
326 }
327 _ => Array2::eye(target_matrix_clone.dim().0), };
329
330 let diff = &target_matrix_clone - &gate_matrix;
332 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
333 };
334
335 let mut options = DifferentialEvolutionOptions::default();
337 options.popsize = 50;
338 options.maxiter = self.config.max_iterations;
339 options.tol = self.config.tolerance;
340
341 let de_bounds: Vec<(f64, f64)> = bounds
342 .into_iter()
343 .map(|(low, high)| {
344 (
345 low.unwrap_or(-std::f64::consts::PI),
346 high.unwrap_or(std::f64::consts::PI),
347 )
348 })
349 .collect();
350
351 let result =
352 differential_evolution(objective, &de_bounds, Some(options), None).map_err(|e| {
353 QuantRS2Error::InvalidInput(format!("Parameter optimization failed: {:?}", e))
354 })?;
355
356 if result.fun > self.config.tolerance {
357 return Ok(None);
359 }
360
361 Ok(Some(CompressedGate::Parameterized {
362 gate_type,
363 parameters: result.x.to_vec(),
364 qubits: vec![], }))
366 }
367
368 fn merge_adjacent_gates(
370 &self,
371 gates: &[Box<dyn GateOp>],
372 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
373 let mut merged = Vec::new();
374 let mut i = 0;
375
376 while i < gates.len() {
377 if i + 1 < gates.len() {
378 if self.can_merge(gates[i].as_ref(), gates[i + 1].as_ref()) {
380 let combined =
382 self.merge_two_gates(gates[i].as_ref(), gates[i + 1].as_ref())?;
383 merged.push(combined);
384 i += 2;
385 } else {
386 merged.push(gates[i].clone_gate());
387 i += 1;
388 }
389 } else {
390 merged.push(gates[i].clone_gate());
391 i += 1;
392 }
393 }
394
395 Ok(merged)
396 }
397
398 fn can_merge(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
400 gate1.name() == gate2.name()
407 }
408
409 fn merge_two_gates(
411 &self,
412 gate1: &dyn GateOp,
413 gate2: &dyn GateOp,
414 ) -> QuantRS2Result<Box<dyn GateOp>> {
415 let matrix1_vec = gate1.matrix()?;
417 let matrix2_vec = gate2.matrix()?;
418
419 let n = (matrix1_vec.len() as f64).sqrt() as usize;
421 let mut matrix1 = Array2::zeros((n, n));
422 let mut matrix2 = Array2::zeros((n, n));
423
424 for j in 0..n {
425 for i in 0..n {
426 matrix1[(i, j)] = matrix1_vec[j * n + i];
427 matrix2[(i, j)] = matrix2_vec[j * n + i];
428 }
429 }
430
431 let combined_matrix = matrix2.dot(&matrix1);
433
434 Ok(Box::new(CustomGate::new(
436 format!("{}_{}_merged", gate1.name(), gate2.name()),
437 combined_matrix,
438 )))
439 }
440
441 fn compute_matrix_hash(&self, matrix: &ArrayView2<Complex64>) -> u64 {
443 use std::collections::hash_map::DefaultHasher;
444 use std::hash::{Hash, Hasher};
445
446 let mut hasher = DefaultHasher::new();
447 for elem in matrix.iter() {
448 elem.re.to_bits().hash(&mut hasher);
450 elem.im.to_bits().hash(&mut hasher);
451 }
452 hasher.finish()
453 }
454
455 fn find_effective_rank(
457 &self,
458 s_real: &scirs2_core::ndarray::Array1<f64>,
459 s_imag: &scirs2_core::ndarray::Array1<f64>,
460 ) -> QuantRS2Result<usize> {
461 let max_singular = s_real
462 .iter()
463 .chain(s_imag.iter())
464 .map(|s| s.abs())
465 .fold(0.0, f64::max);
466
467 let threshold = max_singular * self.config.tolerance;
468
469 let rank = s_real
470 .iter()
471 .zip(s_imag.iter())
472 .take_while(|(sr, si)| sr.abs() > threshold || si.abs() > threshold)
473 .count();
474
475 Ok(rank.max(1))
476 }
477
478 fn combine_complex(
480 &self,
481 real: &Array2<f64>,
482 imag: &Array2<f64>,
483 rank: usize,
484 ) -> QuantRS2Result<Array2<Complex64>> {
485 let (rows, _) = real.dim();
486 let result = Array2::from_shape_fn((rows, rank), |(i, j)| {
487 Complex64::new(real[(i, j)], imag[(i, j)])
488 });
489 Ok(result)
490 }
491
492 fn combine_complex_with_singular(
494 &self,
495 vt_real: &Array2<f64>,
496 vt_imag: &Array2<f64>,
497 s_real: &scirs2_core::ndarray::Array1<f64>,
498 s_imag: &scirs2_core::ndarray::Array1<f64>,
499 rank: usize,
500 ) -> QuantRS2Result<Array2<Complex64>> {
501 let (_, cols) = vt_real.dim();
502 let result = Array2::from_shape_fn((rank, cols), |(i, j)| {
503 let s = Complex64::new(s_real[i], s_imag[i]);
504 let v = Complex64::new(vt_real[(i, j)], vt_imag[(i, j)]);
505 s * v
506 });
507 Ok(result)
508 }
509
510 #[allow(dead_code)]
512 fn tensor_to_complex_matrix(&self, tensor: &[f64]) -> QuantRS2Result<Array2<Complex64>> {
513 let size = (tensor.len() / 2) as f64;
514 let dim = size.sqrt() as usize;
515
516 let mut matrix = Array2::zeros((dim, dim));
517 for i in 0..dim {
518 for j in 0..dim {
519 let idx = (i * dim + j) * 2;
520 matrix[(i, j)] = Complex64::new(tensor[idx], tensor[idx + 1]);
521 }
522 }
523
524 Ok(matrix)
525 }
526
527 #[allow(dead_code)]
529 fn tensor_to_complex_matrix_from_array(
530 &self,
531 tensor: &scirs2_core::ndarray::ArrayD<f64>,
532 ) -> QuantRS2Result<Array2<Complex64>> {
533 let elements: Vec<f64> = tensor.iter().cloned().collect();
535 let size = elements.len() as f64;
536 let dim = size.sqrt() as usize;
537
538 if dim * dim != elements.len() {
539 let dim = (size.sqrt().ceil()) as usize;
541 let mut matrix = Array2::zeros((dim, dim));
542 for (idx, &val) in elements.iter().enumerate() {
543 let i = idx / dim;
544 let j = idx % dim;
545 if i < dim && j < dim {
546 matrix[(i, j)] = Complex64::new(val, 0.0);
547 }
548 }
549 Ok(matrix)
550 } else {
551 let mut matrix = Array2::zeros((dim, dim));
552 for i in 0..dim {
553 for j in 0..dim {
554 let idx = i * dim + j;
555 matrix[(i, j)] = Complex64::new(elements[idx], 0.0);
556 }
557 }
558 Ok(matrix)
559 }
560 }
561
562 fn identify_gate_type(&self, gate: &dyn GateOp) -> String {
564 let name = gate.name();
566 if name.contains("rot") || name.contains("Rot") {
567 "rotation".to_string()
568 } else if name.contains("phase") || name.contains("Phase") {
569 "phase".to_string()
570 } else {
571 "general".to_string()
572 }
573 }
574
575 #[allow(dead_code)]
577 fn evaluate_gate_parameters(
578 &self,
579 target: &Array2<Complex64>,
580 gate_type: &str,
581 params: &[f64],
582 ) -> f64 {
583 let gate_matrix = match gate_type {
585 "rotation" => self.rotation_matrix_from_params(params, target.dim().0),
586 "phase" => self.phase_matrix_from_params(params, target.dim().0),
587 _ => self.general_matrix_from_params(params, target.dim().0),
588 };
589
590 let diff = target - &gate_matrix;
592 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
593 }
594
595 fn rotation_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
596 Array2::eye(dim)
599 }
600
601 fn phase_matrix_from_params(&self, params: &[f64], dim: usize) -> Array2<Complex64> {
602 let mut matrix = Array2::eye(dim);
603 if !params.is_empty() {
604 let phase = Complex64::from_polar(1.0, params[0]);
605 matrix[(dim - 1, dim - 1)] = phase;
606 }
607 matrix
608 }
609
610 #[allow(dead_code)]
611 fn general_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
612 Array2::eye(dim)
614 }
615
616 fn try_runtime_compression(&self, gate: &dyn GateOp) -> QuantRS2Result<Option<CompressedGate>> {
618 let matrix_vec = gate.matrix()?;
619 let n = (matrix_vec.len() as f64).sqrt() as usize;
620
621 let metadata = GateMetadata {
623 name: gate.name().to_string(),
624 num_qubits: gate.num_qubits(),
625 qubits: gate.qubits(),
626 matrix_dims: (n, n),
627 sparsity_ratio: self.calculate_sparsity_ratio(&matrix_vec),
628 is_unitary: self.check_unitary(&matrix_vec, n),
629 };
630
631 let matrix_bytes = self.serialize_matrix(&matrix_vec)?;
633
634 let best_compression = self.find_best_compression(&matrix_bytes, &metadata)?;
636
637 if best_compression.compression_ratio < 0.8 {
639 Ok(Some(CompressedGate::RuntimeCompressed {
640 compressed_data: best_compression.data,
641 compression_type: best_compression.compression_type,
642 original_size: matrix_bytes.len(),
643 gate_metadata: metadata,
644 }))
645 } else {
646 Ok(None)
647 }
648 }
649
650 fn calculate_sparsity_ratio(&self, matrix: &[Complex64]) -> f64 {
652 let zero_count = matrix
653 .iter()
654 .filter(|&c| c.norm() < self.config.tolerance)
655 .count();
656 zero_count as f64 / matrix.len() as f64
657 }
658
659 fn check_unitary(&self, matrix: &[Complex64], n: usize) -> bool {
661 if n > 8 {
664 return false; }
666
667 let mut u = Array2::zeros((n, n));
669 for j in 0..n {
670 for i in 0..n {
671 u[(i, j)] = matrix[j * n + i];
672 }
673 }
674
675 let u_dagger = u.t().mapv(|c| c.conj());
677 let product = u_dagger.dot(&u);
678
679 let identity = Array2::<Complex64>::eye(n);
681 let diff = &product - &identity;
682 let frobenius_norm: f64 = diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
683
684 frobenius_norm < self.config.tolerance
685 }
686
687 fn serialize_matrix(&self, matrix: &[Complex64]) -> QuantRS2Result<Vec<u8>> {
689 let mut bytes = Vec::with_capacity(matrix.len() * 16); for &complex in matrix {
692 bytes.extend_from_slice(&complex.re.to_le_bytes());
693 bytes.extend_from_slice(&complex.im.to_le_bytes());
694 }
695
696 Ok(bytes)
697 }
698
699 fn find_best_compression(
701 &self,
702 data: &[u8],
703 metadata: &GateMetadata,
704 ) -> QuantRS2Result<CompressionResult> {
705 let mut best_compression = CompressionResult {
706 data: data.to_vec(),
707 compression_type: CompressionType::None,
708 compression_ratio: 1.0,
709 };
710
711 if let Ok(zlib_compressed) = self.compress_zlib(data) {
713 let ratio = zlib_compressed.len() as f64 / data.len() as f64;
714 if ratio < best_compression.compression_ratio {
715 best_compression = CompressionResult {
716 data: zlib_compressed,
717 compression_type: CompressionType::Zlib,
718 compression_ratio: ratio,
719 };
720 }
721 }
722
723 if let Ok(lz4_compressed) = self.compress_lz4(data) {
725 let ratio = lz4_compressed.len() as f64 / data.len() as f64;
726 if ratio < best_compression.compression_ratio {
727 best_compression = CompressionResult {
728 data: lz4_compressed,
729 compression_type: CompressionType::LZ4,
730 compression_ratio: ratio,
731 };
732 }
733 }
734
735 if metadata.sparsity_ratio > 0.3 {
737 if let Ok(quantum_compressed) = self.compress_quantum_optimized(data, metadata) {
738 let ratio = quantum_compressed.len() as f64 / data.len() as f64;
739 if ratio < best_compression.compression_ratio {
740 best_compression = CompressionResult {
741 data: quantum_compressed,
742 compression_type: CompressionType::QuantumOptimized,
743 compression_ratio: ratio,
744 };
745 }
746 }
747 }
748
749 Ok(best_compression)
750 }
751
752 fn compress_zlib(&self, _data: &[u8]) -> QuantRS2Result<Vec<u8>> {
754 #[cfg(feature = "compression")]
755 {
756 use std::io::Write;
757 let mut encoder =
758 flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
759 encoder.write_all(_data).map_err(|e| {
760 QuantRS2Error::RuntimeError(format!("Zlib compression failed: {}", e))
761 })?;
762
763 encoder
764 .finish()
765 .map_err(|e| QuantRS2Error::RuntimeError(format!("Zlib compression failed: {}", e)))
766 }
767
768 #[cfg(not(feature = "compression"))]
769 {
770 Ok(_data.to_vec())
772 }
773 }
774
775 fn compress_lz4(&self, data: &[u8]) -> QuantRS2Result<Vec<u8>> {
777 let mut compressed = Vec::new();
782 let mut i = 0;
783
784 while i < data.len() {
785 let byte = data[i];
786 let mut count = 1;
787
788 while i + count < data.len() && data[i + count] == byte && count < 255 {
789 count += 1;
790 }
791
792 if count > 3 {
793 compressed.push(0xFF); compressed.push(count as u8);
796 compressed.push(byte);
797 } else {
798 for _ in 0..count {
800 compressed.push(byte);
801 }
802 }
803
804 i += count;
805 }
806
807 Ok(compressed)
808 }
809
810 fn compress_quantum_optimized(
812 &self,
813 data: &[u8],
814 metadata: &GateMetadata,
815 ) -> QuantRS2Result<Vec<u8>> {
816 let mut compressed = Vec::new();
818
819 compressed.extend_from_slice(&(metadata.num_qubits as u32).to_le_bytes());
821 compressed.extend_from_slice(&metadata.sparsity_ratio.to_le_bytes());
822
823 if metadata.sparsity_ratio > 0.5 {
825 let complex_data = self.deserialize_matrix_from_bytes(data)?;
826 let _n = metadata.matrix_dims.0;
827
828 let mut non_zero_count = 0u32;
830 let mut non_zero_data = Vec::new();
831
832 for (idx, &complex) in complex_data.iter().enumerate() {
833 if complex.norm() >= self.config.tolerance {
834 non_zero_data.extend_from_slice(&(idx as u32).to_le_bytes());
835 non_zero_data.extend_from_slice(&complex.re.to_le_bytes());
836 non_zero_data.extend_from_slice(&complex.im.to_le_bytes());
837 non_zero_count += 1;
838 }
839 }
840
841 compressed.extend_from_slice(&non_zero_count.to_le_bytes());
842 compressed.extend_from_slice(&non_zero_data);
843 } else {
844 compressed.extend_from_slice(data);
846 }
847
848 Ok(compressed)
849 }
850
851 fn deserialize_matrix_from_bytes(&self, bytes: &[u8]) -> QuantRS2Result<Vec<Complex64>> {
853 if bytes.len() % 16 != 0 {
854 return Err(QuantRS2Error::InvalidInput(
855 "Invalid byte length for Complex64 array".to_string(),
856 ));
857 }
858
859 let mut matrix = Vec::with_capacity(bytes.len() / 16);
860
861 for chunk in bytes.chunks_exact(16) {
862 let re = f64::from_le_bytes([
863 chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
864 ]);
865 let im = f64::from_le_bytes([
866 chunk[8], chunk[9], chunk[10], chunk[11], chunk[12], chunk[13], chunk[14],
867 chunk[15],
868 ]);
869
870 matrix.push(Complex64::new(re, im));
871 }
872
873 Ok(matrix)
874 }
875
876 pub fn decompress_gate(&self, compressed: &CompressedGate) -> QuantRS2Result<Box<dyn GateOp>> {
878 match compressed {
879 CompressedGate::RuntimeCompressed {
880 compressed_data,
881 compression_type,
882 original_size,
883 gate_metadata,
884 } => {
885 let decompressed_bytes = self.decompress_data(
887 compressed_data,
888 *compression_type,
889 *original_size,
890 gate_metadata,
891 )?;
892
893 let matrix = self.deserialize_matrix_from_bytes(&decompressed_bytes)?;
895
896 let n = gate_metadata.matrix_dims.0;
898 let mut matrix_2d = Array2::zeros((n, n));
899 for j in 0..n {
900 for i in 0..n {
901 matrix_2d[(i, j)] = matrix[j * n + i];
902 }
903 }
904
905 Ok(Box::new(CustomGate::with_qubits(
906 gate_metadata.name.clone(),
907 matrix_2d,
908 gate_metadata.qubits.clone(),
909 )))
910 }
911 CompressedGate::LowRank { left, right, .. } => {
912 let reconstructed = left.dot(&right.t());
914 Ok(Box::new(CustomGate::new(
915 "LowRank".to_string(),
916 reconstructed,
917 )))
918 }
919 CompressedGate::Tucker { core, factors: _ } => {
920 Ok(Box::new(CustomGate::new(
923 "Tucker".to_string(),
924 core.clone(),
925 )))
926 }
927 CompressedGate::Parameterized {
928 gate_type,
929 parameters,
930 qubits,
931 } => {
932 self.reconstruct_parameterized_gate(gate_type, parameters, qubits)
934 }
935 CompressedGate::Original(gate) => Ok(gate.clone_gate()),
936 }
937 }
938
939 fn decompress_data(
941 &self,
942 compressed_data: &[u8],
943 compression_type: CompressionType,
944 original_size: usize,
945 metadata: &GateMetadata,
946 ) -> QuantRS2Result<Vec<u8>> {
947 match compression_type {
948 CompressionType::None => Ok(compressed_data.to_vec()),
949 CompressionType::Zlib => self.decompress_zlib(compressed_data),
950 CompressionType::LZ4 => self.decompress_lz4(compressed_data, original_size),
951 CompressionType::QuantumOptimized => {
952 self.decompress_quantum_optimized(compressed_data, metadata)
953 }
954 CompressionType::Huffman => {
955 Ok(compressed_data.to_vec())
957 }
958 }
959 }
960
961 fn decompress_zlib(&self, compressed_data: &[u8]) -> QuantRS2Result<Vec<u8>> {
963 #[cfg(feature = "compression")]
964 {
965 use std::io::Read;
966
967 let mut decoder = flate2::read::ZlibDecoder::new(compressed_data);
968 let mut decompressed = Vec::new();
969
970 decoder.read_to_end(&mut decompressed).map_err(|e| {
971 QuantRS2Error::RuntimeError(format!("Zlib decompression failed: {}", e))
972 })?;
973
974 Ok(decompressed)
975 }
976
977 #[cfg(not(feature = "compression"))]
978 {
979 Ok(compressed_data.to_vec())
981 }
982 }
983
984 fn decompress_lz4(
986 &self,
987 compressed_data: &[u8],
988 original_size: usize,
989 ) -> QuantRS2Result<Vec<u8>> {
990 let mut decompressed = Vec::with_capacity(original_size);
992 let mut i = 0;
993
994 while i < compressed_data.len() {
995 if compressed_data[i] == 0xFF && i + 2 < compressed_data.len() {
996 let count = compressed_data[i + 1] as usize;
998 let byte = compressed_data[i + 2];
999
1000 for _ in 0..count {
1001 decompressed.push(byte);
1002 }
1003
1004 i += 3;
1005 } else {
1006 decompressed.push(compressed_data[i]);
1008 i += 1;
1009 }
1010 }
1011
1012 Ok(decompressed)
1013 }
1014
1015 fn decompress_quantum_optimized(
1017 &self,
1018 compressed_data: &[u8],
1019 metadata: &GateMetadata,
1020 ) -> QuantRS2Result<Vec<u8>> {
1021 if compressed_data.len() < 12 {
1022 return Err(QuantRS2Error::InvalidInput(
1023 "Invalid quantum-optimized compressed data".to_string(),
1024 ));
1025 }
1026
1027 let mut cursor = 0;
1028
1029 let _num_qubits = u32::from_le_bytes([
1031 compressed_data[cursor],
1032 compressed_data[cursor + 1],
1033 compressed_data[cursor + 2],
1034 compressed_data[cursor + 3],
1035 ]);
1036 cursor += 4;
1037
1038 let sparsity_ratio = f64::from_le_bytes([
1039 compressed_data[cursor],
1040 compressed_data[cursor + 1],
1041 compressed_data[cursor + 2],
1042 compressed_data[cursor + 3],
1043 compressed_data[cursor + 4],
1044 compressed_data[cursor + 5],
1045 compressed_data[cursor + 6],
1046 compressed_data[cursor + 7],
1047 ]);
1048 cursor += 8;
1049
1050 if sparsity_ratio > 0.5 {
1051 let non_zero_count = u32::from_le_bytes([
1053 compressed_data[cursor],
1054 compressed_data[cursor + 1],
1055 compressed_data[cursor + 2],
1056 compressed_data[cursor + 3],
1057 ]);
1058 cursor += 4;
1059
1060 let matrix_size = metadata.matrix_dims.0 * metadata.matrix_dims.1;
1061 let mut matrix = vec![Complex64::new(0.0, 0.0); matrix_size];
1062
1063 for _ in 0..non_zero_count {
1064 let index = u32::from_le_bytes([
1065 compressed_data[cursor],
1066 compressed_data[cursor + 1],
1067 compressed_data[cursor + 2],
1068 compressed_data[cursor + 3],
1069 ]) as usize;
1070 cursor += 4;
1071
1072 let re = f64::from_le_bytes([
1073 compressed_data[cursor],
1074 compressed_data[cursor + 1],
1075 compressed_data[cursor + 2],
1076 compressed_data[cursor + 3],
1077 compressed_data[cursor + 4],
1078 compressed_data[cursor + 5],
1079 compressed_data[cursor + 6],
1080 compressed_data[cursor + 7],
1081 ]);
1082 cursor += 8;
1083
1084 let im = f64::from_le_bytes([
1085 compressed_data[cursor],
1086 compressed_data[cursor + 1],
1087 compressed_data[cursor + 2],
1088 compressed_data[cursor + 3],
1089 compressed_data[cursor + 4],
1090 compressed_data[cursor + 5],
1091 compressed_data[cursor + 6],
1092 compressed_data[cursor + 7],
1093 ]);
1094 cursor += 8;
1095
1096 if index < matrix_size {
1097 matrix[index] = Complex64::new(re, im);
1098 }
1099 }
1100
1101 self.serialize_matrix(&matrix)
1103 } else {
1104 Ok(compressed_data[cursor..].to_vec())
1106 }
1107 }
1108
1109 fn reconstruct_parameterized_gate(
1111 &self,
1112 gate_type: &str,
1113 parameters: &[f64],
1114 qubits: &[QubitId],
1115 ) -> QuantRS2Result<Box<dyn GateOp>> {
1116 match gate_type {
1117 "rotation" => {
1118 if parameters.len() >= 3 && !qubits.is_empty() {
1119 let matrix = self.rotation_matrix_from_params(parameters, 2);
1121 Ok(Box::new(CustomGate::with_qubits(
1122 "Rotation".to_string(),
1123 matrix,
1124 qubits.to_vec(),
1125 )))
1126 } else {
1127 Err(QuantRS2Error::InvalidInput(
1128 "Invalid rotation parameters".to_string(),
1129 ))
1130 }
1131 }
1132 "phase" => {
1133 if !parameters.is_empty() && !qubits.is_empty() {
1134 let matrix = self.phase_matrix_from_params(parameters, 2);
1135 Ok(Box::new(CustomGate::with_qubits(
1136 "Phase".to_string(),
1137 matrix,
1138 qubits.to_vec(),
1139 )))
1140 } else {
1141 Err(QuantRS2Error::InvalidInput(
1142 "Invalid phase parameters".to_string(),
1143 ))
1144 }
1145 }
1146 _ => {
1147 let dim = 1 << qubits.len();
1149 let matrix = Array2::eye(dim);
1150 Ok(Box::new(CustomGate::with_qubits(
1151 gate_type.to_string(),
1152 matrix,
1153 qubits.to_vec(),
1154 )))
1155 }
1156 }
1157 }
1158}
1159
1160struct CompressionResult {
1162 data: Vec<u8>,
1163 compression_type: CompressionType,
1164 compression_ratio: f64,
1165}
1166
1167#[derive(Debug, Clone)]
1169pub struct CustomGate {
1170 name: String,
1171 matrix: Array2<Complex64>,
1172 qubits: Vec<QubitId>,
1173}
1174
1175impl CustomGate {
1176 pub fn new(name: String, matrix: Array2<Complex64>) -> Self {
1177 let n_qubits = (matrix.dim().0 as f64).log2() as usize;
1179 let qubits = (0..n_qubits).map(|i| QubitId::new(i as u32)).collect();
1180 Self {
1181 name,
1182 matrix,
1183 qubits,
1184 }
1185 }
1186
1187 pub fn with_qubits(name: String, matrix: Array2<Complex64>, qubits: Vec<QubitId>) -> Self {
1188 Self {
1189 name,
1190 matrix,
1191 qubits,
1192 }
1193 }
1194}
1195
1196impl GateOp for CustomGate {
1197 fn name(&self) -> &'static str {
1198 Box::leak(self.name.clone().into_boxed_str())
1200 }
1201
1202 fn qubits(&self) -> Vec<QubitId> {
1203 self.qubits.clone()
1204 }
1205
1206 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
1207 let mut result = Vec::with_capacity(self.matrix.len());
1209 let (rows, cols) = self.matrix.dim();
1210 for j in 0..cols {
1211 for i in 0..rows {
1212 result.push(self.matrix[(i, j)]);
1213 }
1214 }
1215 Ok(result)
1216 }
1217
1218 fn as_any(&self) -> &dyn Any {
1219 self
1220 }
1221
1222 fn clone_gate(&self) -> Box<dyn GateOp> {
1223 Box::new(self.clone())
1224 }
1225}
1226
1227#[derive(Debug, Clone, Default)]
1229pub struct CompressionStats {
1230 pub original_gates: usize,
1231 pub compressed_gates: usize,
1232 pub low_rank_compressions: usize,
1233 pub tucker_compressions: usize,
1234 pub parameterized_compressions: usize,
1235 pub compression_ratio: f64,
1236 pub total_parameters_before: usize,
1237 pub total_parameters_after: usize,
1238}
1239
1240impl GateSequenceCompressor {
1241 pub fn get_stats(
1243 &self,
1244 original: &[Box<dyn GateOp>],
1245 compressed: &[CompressedGate],
1246 ) -> CompressionStats {
1247 let mut stats = CompressionStats::default();
1248 stats.original_gates = original.len();
1249 stats.compressed_gates = compressed.len();
1250
1251 for gate in compressed {
1252 match gate {
1253 CompressedGate::LowRank { left, right, .. } => {
1254 stats.low_rank_compressions += 1;
1255 stats.total_parameters_after += (left.len() + right.len()) * 2;
1256 }
1257 CompressedGate::Tucker { core, factors } => {
1258 stats.tucker_compressions += 1;
1259 stats.total_parameters_after += core.len() * 2;
1260 stats.total_parameters_after +=
1261 factors.iter().map(|f| f.len() * 2).sum::<usize>();
1262 }
1263 CompressedGate::Parameterized { parameters, .. } => {
1264 stats.parameterized_compressions += 1;
1265 stats.total_parameters_after += parameters.len();
1266 }
1267 CompressedGate::RuntimeCompressed {
1268 compressed_data, ..
1269 } => {
1270 stats.total_parameters_after += compressed_data.len();
1272 }
1273 CompressedGate::Original(gate) => {
1274 if let Ok(matrix_vec) = gate.matrix() {
1275 let size = (matrix_vec.len() as f64).sqrt() as usize;
1276 stats.total_parameters_after += size * size * 2;
1277 }
1278 }
1279 }
1280 }
1281
1282 for gate in original {
1283 if let Ok(matrix_vec) = gate.matrix() {
1284 let size = (matrix_vec.len() as f64).sqrt() as usize;
1285 stats.total_parameters_before += size * size * 2;
1286 }
1287 }
1288
1289 stats.compression_ratio = if stats.total_parameters_before > 0 {
1290 stats.total_parameters_after as f64 / stats.total_parameters_before as f64
1291 } else {
1292 1.0
1293 };
1294
1295 stats
1296 }
1297}
1298
1299#[cfg(test)]
1300mod tests {
1301 use super::*;
1302 use crate::gate::single::{Hadamard, PauliX, PauliZ};
1303 use crate::qubit::QubitId;
1304
1305 #[test]
1306 fn test_gate_compression() {
1307 let config = CompressionConfig::default();
1308 let mut compressor = GateSequenceCompressor::new(config);
1309
1310 let h_gate = Hadamard {
1312 target: QubitId::new(0),
1313 };
1314 let compressed = compressor.compress_gate(&h_gate).unwrap();
1315
1316 match compressed {
1317 CompressedGate::Original(_) => {
1318 }
1320 CompressedGate::RuntimeCompressed { .. } => {
1321 }
1323 _ => panic!("H gate shouldn't be significantly compressed"),
1324 }
1325 }
1326
1327 #[test]
1328 fn test_sequence_compression() {
1329 let config = CompressionConfig::default();
1330 let mut compressor = GateSequenceCompressor::new(config);
1331
1332 let gates: Vec<Box<dyn GateOp>> = vec![
1334 Box::new(Hadamard {
1335 target: QubitId::new(0),
1336 }),
1337 Box::new(PauliX {
1338 target: QubitId::new(0),
1339 }),
1340 Box::new(Hadamard {
1341 target: QubitId::new(0),
1342 }),
1343 ];
1344
1345 let compressed = compressor.compress_sequence(&gates).unwrap();
1346 assert!(compressed.len() <= gates.len());
1347 }
1348
1349 #[test]
1350 fn test_compression_stats() {
1351 let config = CompressionConfig::default();
1352 let mut compressor = GateSequenceCompressor::new(config);
1353
1354 let gates: Vec<Box<dyn GateOp>> = vec![
1355 Box::new(Hadamard {
1356 target: QubitId::new(0),
1357 }),
1358 Box::new(PauliZ {
1359 target: QubitId::new(0),
1360 }),
1361 ];
1362
1363 let compressed = compressor.compress_sequence(&gates).unwrap();
1364 let stats = compressor.get_stats(&gates, &compressed);
1365
1366 assert_eq!(stats.original_gates, 2);
1367 assert!(stats.compression_ratio >= 0.0);
1369 }
1370}