1use crate::{
7 error::{QuantRS2Error, QuantRS2Result},
8 gate::GateOp,
9 matrix_ops::matrices_approx_equal,
10 qubit::QubitId,
11};
12use ndarray::{Array2, ArrayView2};
13use num_complex::Complex64;
14use scirs2_linalg::lowrank::{randomized_svd, truncated_svd};
15use scirs2_optimize::prelude::DifferentialEvolutionOptions;
16use scirs2_optimize::differential_evolution;
19use std::any::Any;
20use std::collections::HashMap;
21
22#[derive(Debug, Clone)]
24pub struct CompressionConfig {
25 pub tolerance: f64,
27 pub max_rank: Option<usize>,
29 pub use_randomized: bool,
31 pub max_iterations: usize,
33 pub num_threads: Option<usize>,
35}
36
37impl Default for CompressionConfig {
38 fn default() -> Self {
39 Self {
40 tolerance: 1e-10,
41 max_rank: None,
42 use_randomized: true,
43 max_iterations: 1000,
44 num_threads: None,
45 }
46 }
47}
48
49pub struct GateSequenceCompressor {
51 config: CompressionConfig,
52 compression_cache: HashMap<u64, CompressedGate>,
54}
55
56#[derive(Debug, Clone)]
58pub enum CompressedGate {
59 LowRank {
61 left: Array2<Complex64>,
62 right: Array2<Complex64>,
63 rank: usize,
64 },
65 Tucker {
67 core: Array2<Complex64>,
68 factors: Vec<Array2<Complex64>>,
69 },
70 Parameterized {
72 gate_type: String,
73 parameters: Vec<f64>,
74 qubits: Vec<QubitId>,
75 },
76 RuntimeCompressed {
78 compressed_data: Vec<u8>,
79 compression_type: CompressionType,
80 original_size: usize,
81 gate_metadata: GateMetadata,
82 },
83 Original(Box<dyn GateOp>),
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum CompressionType {
90 None,
92 Zlib,
94 LZ4,
96 Huffman,
98 QuantumOptimized,
100}
101
102#[derive(Debug, Clone)]
104pub struct GateMetadata {
105 pub name: String,
107 pub num_qubits: usize,
109 pub qubits: Vec<QubitId>,
111 pub matrix_dims: (usize, usize),
113 pub sparsity_ratio: f64,
115 pub is_unitary: bool,
117}
118
119impl GateSequenceCompressor {
120 pub fn new(config: CompressionConfig) -> Self {
122 Self {
123 config,
124 compression_cache: HashMap::new(),
125 }
126 }
127
128 pub fn compress_gate(&mut self, gate: &dyn GateOp) -> QuantRS2Result<CompressedGate> {
130 let matrix_vec = gate.matrix()?;
131
132 let n = (matrix_vec.len() as f64).sqrt() as usize;
134 let mut matrix = Array2::zeros((n, n));
135 for j in 0..n {
136 for i in 0..n {
137 matrix[(i, j)] = matrix_vec[j * n + i];
138 }
139 }
140
141 let matrix_view = matrix.view();
142 let hash = self.compute_matrix_hash(&matrix_view);
143
144 if let Some(compressed) = self.compression_cache.get(&hash) {
146 return Ok(compressed.clone());
147 }
148
149 let compressed = if let Some(low_rank) = self.try_low_rank_approximation(&matrix_view)? {
151 low_rank
152 } else if let Some(tucker) = self.try_tucker_decomposition(&matrix_view)? {
153 tucker
154 } else if let Some(param) = self.try_parameterized_compression(gate)? {
155 param
156 } else if let Some(runtime_compressed) = self.try_runtime_compression(gate)? {
157 runtime_compressed
158 } else {
159 CompressedGate::Original(gate.clone_gate())
160 };
161
162 self.compression_cache.insert(hash, compressed.clone());
164
165 Ok(compressed)
166 }
167
168 pub fn compress_sequence(
170 &mut self,
171 gates: &[Box<dyn GateOp>],
172 ) -> QuantRS2Result<Vec<CompressedGate>> {
173 if let Some(threads) = self.config.num_threads {
175 rayon::ThreadPoolBuilder::new()
176 .num_threads(threads)
177 .build_global()
178 .map_err(|e| QuantRS2Error::InvalidInput(e.to_string()))?;
179 }
180
181 let merged = self.merge_adjacent_gates(gates)?;
183
184 let compressed: Result<Vec<_>, _> = merged
186 .iter()
187 .map(|gate| self.compress_gate(gate.as_ref()))
188 .collect();
189
190 compressed
191 }
192
193 fn try_low_rank_approximation(
195 &self,
196 matrix: &ArrayView2<Complex64>,
197 ) -> QuantRS2Result<Option<CompressedGate>> {
198 let (rows, cols) = matrix.dim();
199 if rows != cols || rows < 4 {
200 return Ok(None);
202 }
203
204 let real_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].re);
206 let imag_part = Array2::from_shape_fn((rows, cols), |(i, j)| matrix[(i, j)].im);
207
208 let target_rank = self.config.max_rank.unwrap_or(rows / 2);
210
211 let (u_real, s_real, vt_real) = if self.config.use_randomized {
213 randomized_svd(&real_part.view(), target_rank, Some(10), Some(2))
214 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
215 } else {
216 truncated_svd(&real_part.view(), target_rank)
217 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
218 };
219
220 let (u_imag, s_imag, vt_imag) = if self.config.use_randomized {
221 randomized_svd(&imag_part.view(), target_rank, Some(10), Some(2))
222 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
223 } else {
224 truncated_svd(&imag_part.view(), target_rank)
225 .map_err(|e| QuantRS2Error::InvalidInput(format!("SVD failed: {}", e)))?
226 };
227
228 let effective_rank = self.find_effective_rank(&s_real, &s_imag)?;
230
231 if effective_rank >= rows * 3 / 4 {
232 return Ok(None);
234 }
235
236 let left = self.combine_complex(&u_real, &u_imag, effective_rank)?;
238 let right = self.combine_complex_with_singular(
239 &vt_real,
240 &vt_imag,
241 &s_real,
242 &s_imag,
243 effective_rank,
244 )?;
245
246 let approx = left.dot(&right.t());
248 if !matrices_approx_equal(&approx.view(), matrix, self.config.tolerance) {
249 return Ok(None);
250 }
251
252 Ok(Some(CompressedGate::LowRank {
253 left,
254 right,
255 rank: effective_rank,
256 }))
257 }
258
259 fn try_tucker_decomposition(
261 &self,
262 _matrix: &ArrayView2<Complex64>,
263 ) -> QuantRS2Result<Option<CompressedGate>> {
264 Ok(None)
267 }
268
269 fn try_parameterized_compression(
271 &self,
272 gate: &dyn GateOp,
273 ) -> QuantRS2Result<Option<CompressedGate>> {
274 let matrix_vec = gate.matrix()?;
279 let n = (matrix_vec.len() as f64).sqrt() as usize;
280
281 if n > 4 {
282 return Ok(None);
284 }
285
286 let mut target_matrix = Array2::zeros((n, n));
288 for j in 0..n {
289 for i in 0..n {
290 target_matrix[(i, j)] = matrix_vec[j * n + i];
291 }
292 }
293
294 let gate_type = self.identify_gate_type(gate);
295
296 let dim = match gate_type.as_str() {
298 "rotation" => 3, "phase" => 1, _ => 6, };
302 let bounds = vec![(Some(-std::f64::consts::PI), Some(std::f64::consts::PI)); dim];
303
304 let target_matrix_clone = target_matrix.clone();
306 let gate_type_clone = gate_type.clone();
307 let _tolerance = self.config.tolerance;
308
309 let objective = move |x: &ndarray::ArrayView1<f64>| -> f64 {
311 let params: Vec<f64> = x.iter().cloned().collect();
312
313 let gate_matrix = match gate_type_clone.as_str() {
315 "rotation" => Array2::eye(target_matrix_clone.dim().0), "phase" => {
317 let mut matrix = Array2::eye(target_matrix_clone.dim().0);
318 if !params.is_empty() {
319 let phase = Complex64::from_polar(1.0, params[0]);
320 let n = matrix.dim().0;
321 matrix[(n - 1, n - 1)] = phase;
322 }
323 matrix
324 }
325 _ => Array2::eye(target_matrix_clone.dim().0), };
327
328 let diff = &target_matrix_clone - &gate_matrix;
330 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
331 };
332
333 let mut options = DifferentialEvolutionOptions::default();
335 options.popsize = 50;
336 options.maxiter = self.config.max_iterations;
337 options.tol = self.config.tolerance;
338
339 let de_bounds: Vec<(f64, f64)> = bounds
340 .into_iter()
341 .map(|(low, high)| {
342 (
343 low.unwrap_or(-std::f64::consts::PI),
344 high.unwrap_or(std::f64::consts::PI),
345 )
346 })
347 .collect();
348
349 let result =
350 differential_evolution(objective, de_bounds, Some(options), None).map_err(|e| {
351 QuantRS2Error::InvalidInput(format!("Parameter optimization failed: {:?}", e))
352 })?;
353
354 if result.fun > self.config.tolerance {
355 return Ok(None);
357 }
358
359 Ok(Some(CompressedGate::Parameterized {
360 gate_type,
361 parameters: result.x.to_vec(),
362 qubits: vec![], }))
364 }
365
366 fn merge_adjacent_gates(
368 &self,
369 gates: &[Box<dyn GateOp>],
370 ) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
371 let mut merged = Vec::new();
372 let mut i = 0;
373
374 while i < gates.len() {
375 if i + 1 < gates.len() {
376 if self.can_merge(gates[i].as_ref(), gates[i + 1].as_ref()) {
378 let combined =
380 self.merge_two_gates(gates[i].as_ref(), gates[i + 1].as_ref())?;
381 merged.push(combined);
382 i += 2;
383 } else {
384 merged.push(gates[i].clone_gate());
385 i += 1;
386 }
387 } else {
388 merged.push(gates[i].clone_gate());
389 i += 1;
390 }
391 }
392
393 Ok(merged)
394 }
395
396 fn can_merge(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
398 gate1.name() == gate2.name()
405 }
406
407 fn merge_two_gates(
409 &self,
410 gate1: &dyn GateOp,
411 gate2: &dyn GateOp,
412 ) -> QuantRS2Result<Box<dyn GateOp>> {
413 let matrix1_vec = gate1.matrix()?;
415 let matrix2_vec = gate2.matrix()?;
416
417 let n = (matrix1_vec.len() as f64).sqrt() as usize;
419 let mut matrix1 = Array2::zeros((n, n));
420 let mut matrix2 = Array2::zeros((n, n));
421
422 for j in 0..n {
423 for i in 0..n {
424 matrix1[(i, j)] = matrix1_vec[j * n + i];
425 matrix2[(i, j)] = matrix2_vec[j * n + i];
426 }
427 }
428
429 let combined_matrix = matrix2.dot(&matrix1);
431
432 Ok(Box::new(CustomGate::new(
434 format!("{}_{}_merged", gate1.name(), gate2.name()),
435 combined_matrix,
436 )))
437 }
438
439 fn compute_matrix_hash(&self, matrix: &ArrayView2<Complex64>) -> u64 {
441 use std::collections::hash_map::DefaultHasher;
442 use std::hash::{Hash, Hasher};
443
444 let mut hasher = DefaultHasher::new();
445 for elem in matrix.iter() {
446 elem.re.to_bits().hash(&mut hasher);
448 elem.im.to_bits().hash(&mut hasher);
449 }
450 hasher.finish()
451 }
452
453 fn find_effective_rank(
455 &self,
456 s_real: &ndarray::Array1<f64>,
457 s_imag: &ndarray::Array1<f64>,
458 ) -> QuantRS2Result<usize> {
459 let max_singular = s_real
460 .iter()
461 .chain(s_imag.iter())
462 .map(|s| s.abs())
463 .fold(0.0, f64::max);
464
465 let threshold = max_singular * self.config.tolerance;
466
467 let rank = s_real
468 .iter()
469 .zip(s_imag.iter())
470 .take_while(|(sr, si)| sr.abs() > threshold || si.abs() > threshold)
471 .count();
472
473 Ok(rank.max(1))
474 }
475
476 fn combine_complex(
478 &self,
479 real: &Array2<f64>,
480 imag: &Array2<f64>,
481 rank: usize,
482 ) -> QuantRS2Result<Array2<Complex64>> {
483 let (rows, _) = real.dim();
484 let result = Array2::from_shape_fn((rows, rank), |(i, j)| {
485 Complex64::new(real[(i, j)], imag[(i, j)])
486 });
487 Ok(result)
488 }
489
490 fn combine_complex_with_singular(
492 &self,
493 vt_real: &Array2<f64>,
494 vt_imag: &Array2<f64>,
495 s_real: &ndarray::Array1<f64>,
496 s_imag: &ndarray::Array1<f64>,
497 rank: usize,
498 ) -> QuantRS2Result<Array2<Complex64>> {
499 let (_, cols) = vt_real.dim();
500 let result = Array2::from_shape_fn((rank, cols), |(i, j)| {
501 let s = Complex64::new(s_real[i], s_imag[i]);
502 let v = Complex64::new(vt_real[(i, j)], vt_imag[(i, j)]);
503 s * v
504 });
505 Ok(result)
506 }
507
508 #[allow(dead_code)]
510 fn tensor_to_complex_matrix(&self, tensor: &[f64]) -> QuantRS2Result<Array2<Complex64>> {
511 let size = (tensor.len() / 2) as f64;
512 let dim = size.sqrt() as usize;
513
514 let mut matrix = Array2::zeros((dim, dim));
515 for i in 0..dim {
516 for j in 0..dim {
517 let idx = (i * dim + j) * 2;
518 matrix[(i, j)] = Complex64::new(tensor[idx], tensor[idx + 1]);
519 }
520 }
521
522 Ok(matrix)
523 }
524
525 #[allow(dead_code)]
527 fn tensor_to_complex_matrix_from_array(
528 &self,
529 tensor: &ndarray::ArrayD<f64>,
530 ) -> QuantRS2Result<Array2<Complex64>> {
531 let elements: Vec<f64> = tensor.iter().cloned().collect();
533 let size = elements.len() as f64;
534 let dim = size.sqrt() as usize;
535
536 if dim * dim != elements.len() {
537 let dim = (size.sqrt().ceil()) as usize;
539 let mut matrix = Array2::zeros((dim, dim));
540 for (idx, &val) in elements.iter().enumerate() {
541 let i = idx / dim;
542 let j = idx % dim;
543 if i < dim && j < dim {
544 matrix[(i, j)] = Complex64::new(val, 0.0);
545 }
546 }
547 Ok(matrix)
548 } else {
549 let mut matrix = Array2::zeros((dim, dim));
550 for i in 0..dim {
551 for j in 0..dim {
552 let idx = i * dim + j;
553 matrix[(i, j)] = Complex64::new(elements[idx], 0.0);
554 }
555 }
556 Ok(matrix)
557 }
558 }
559
560 fn identify_gate_type(&self, gate: &dyn GateOp) -> String {
562 let name = gate.name();
564 if name.contains("rot") || name.contains("Rot") {
565 "rotation".to_string()
566 } else if name.contains("phase") || name.contains("Phase") {
567 "phase".to_string()
568 } else {
569 "general".to_string()
570 }
571 }
572
573 #[allow(dead_code)]
575 fn evaluate_gate_parameters(
576 &self,
577 target: &Array2<Complex64>,
578 gate_type: &str,
579 params: &[f64],
580 ) -> f64 {
581 let gate_matrix = match gate_type {
583 "rotation" => self.rotation_matrix_from_params(params, target.dim().0),
584 "phase" => self.phase_matrix_from_params(params, target.dim().0),
585 _ => self.general_matrix_from_params(params, target.dim().0),
586 };
587
588 let diff = target - &gate_matrix;
590 diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt()
591 }
592
593 fn rotation_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
594 Array2::eye(dim)
597 }
598
599 fn phase_matrix_from_params(&self, params: &[f64], dim: usize) -> Array2<Complex64> {
600 let mut matrix = Array2::eye(dim);
601 if !params.is_empty() {
602 let phase = Complex64::from_polar(1.0, params[0]);
603 matrix[(dim - 1, dim - 1)] = phase;
604 }
605 matrix
606 }
607
608 #[allow(dead_code)]
609 fn general_matrix_from_params(&self, _params: &[f64], dim: usize) -> Array2<Complex64> {
610 Array2::eye(dim)
612 }
613
614 fn try_runtime_compression(&self, gate: &dyn GateOp) -> QuantRS2Result<Option<CompressedGate>> {
616 let matrix_vec = gate.matrix()?;
617 let n = (matrix_vec.len() as f64).sqrt() as usize;
618
619 let metadata = GateMetadata {
621 name: gate.name().to_string(),
622 num_qubits: gate.num_qubits(),
623 qubits: gate.qubits(),
624 matrix_dims: (n, n),
625 sparsity_ratio: self.calculate_sparsity_ratio(&matrix_vec),
626 is_unitary: self.check_unitary(&matrix_vec, n),
627 };
628
629 let matrix_bytes = self.serialize_matrix(&matrix_vec)?;
631
632 let best_compression = self.find_best_compression(&matrix_bytes, &metadata)?;
634
635 if best_compression.compression_ratio < 0.8 {
637 Ok(Some(CompressedGate::RuntimeCompressed {
638 compressed_data: best_compression.data,
639 compression_type: best_compression.compression_type,
640 original_size: matrix_bytes.len(),
641 gate_metadata: metadata,
642 }))
643 } else {
644 Ok(None)
645 }
646 }
647
648 fn calculate_sparsity_ratio(&self, matrix: &[Complex64]) -> f64 {
650 let zero_count = matrix
651 .iter()
652 .filter(|&c| c.norm() < self.config.tolerance)
653 .count();
654 zero_count as f64 / matrix.len() as f64
655 }
656
657 fn check_unitary(&self, matrix: &[Complex64], n: usize) -> bool {
659 if n > 8 {
662 return false; }
664
665 let mut u = Array2::zeros((n, n));
667 for j in 0..n {
668 for i in 0..n {
669 u[(i, j)] = matrix[j * n + i];
670 }
671 }
672
673 let u_dagger = u.t().mapv(|c| c.conj());
675 let product = u_dagger.dot(&u);
676
677 let identity = Array2::<Complex64>::eye(n);
679 let diff = &product - &identity;
680 let frobenius_norm: f64 = diff.iter().map(|c| c.norm_sqr()).sum::<f64>().sqrt();
681
682 frobenius_norm < self.config.tolerance
683 }
684
685 fn serialize_matrix(&self, matrix: &[Complex64]) -> QuantRS2Result<Vec<u8>> {
687 let mut bytes = Vec::with_capacity(matrix.len() * 16); for &complex in matrix {
690 bytes.extend_from_slice(&complex.re.to_le_bytes());
691 bytes.extend_from_slice(&complex.im.to_le_bytes());
692 }
693
694 Ok(bytes)
695 }
696
697 fn find_best_compression(
699 &self,
700 data: &[u8],
701 metadata: &GateMetadata,
702 ) -> QuantRS2Result<CompressionResult> {
703 let mut best_compression = CompressionResult {
704 data: data.to_vec(),
705 compression_type: CompressionType::None,
706 compression_ratio: 1.0,
707 };
708
709 if let Ok(zlib_compressed) = self.compress_zlib(data) {
711 let ratio = zlib_compressed.len() as f64 / data.len() as f64;
712 if ratio < best_compression.compression_ratio {
713 best_compression = CompressionResult {
714 data: zlib_compressed,
715 compression_type: CompressionType::Zlib,
716 compression_ratio: ratio,
717 };
718 }
719 }
720
721 if let Ok(lz4_compressed) = self.compress_lz4(data) {
723 let ratio = lz4_compressed.len() as f64 / data.len() as f64;
724 if ratio < best_compression.compression_ratio {
725 best_compression = CompressionResult {
726 data: lz4_compressed,
727 compression_type: CompressionType::LZ4,
728 compression_ratio: ratio,
729 };
730 }
731 }
732
733 if metadata.sparsity_ratio > 0.3 {
735 if let Ok(quantum_compressed) = self.compress_quantum_optimized(data, metadata) {
736 let ratio = quantum_compressed.len() as f64 / data.len() as f64;
737 if ratio < best_compression.compression_ratio {
738 best_compression = CompressionResult {
739 data: quantum_compressed,
740 compression_type: CompressionType::QuantumOptimized,
741 compression_ratio: ratio,
742 };
743 }
744 }
745 }
746
747 Ok(best_compression)
748 }
749
750 fn compress_zlib(&self, _data: &[u8]) -> QuantRS2Result<Vec<u8>> {
752 #[cfg(feature = "compression")]
753 {
754 use std::io::Write;
755 let mut encoder =
756 flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
757 encoder.write_all(_data).map_err(|e| {
758 QuantRS2Error::RuntimeError(format!("Zlib compression failed: {}", e))
759 })?;
760
761 encoder
762 .finish()
763 .map_err(|e| QuantRS2Error::RuntimeError(format!("Zlib compression failed: {}", e)))
764 }
765
766 #[cfg(not(feature = "compression"))]
767 {
768 Ok(_data.to_vec())
770 }
771 }
772
773 fn compress_lz4(&self, data: &[u8]) -> QuantRS2Result<Vec<u8>> {
775 let mut compressed = Vec::new();
780 let mut i = 0;
781
782 while i < data.len() {
783 let byte = data[i];
784 let mut count = 1;
785
786 while i + count < data.len() && data[i + count] == byte && count < 255 {
787 count += 1;
788 }
789
790 if count > 3 {
791 compressed.push(0xFF); compressed.push(count as u8);
794 compressed.push(byte);
795 } else {
796 for _ in 0..count {
798 compressed.push(byte);
799 }
800 }
801
802 i += count;
803 }
804
805 Ok(compressed)
806 }
807
808 fn compress_quantum_optimized(
810 &self,
811 data: &[u8],
812 metadata: &GateMetadata,
813 ) -> QuantRS2Result<Vec<u8>> {
814 let mut compressed = Vec::new();
816
817 compressed.extend_from_slice(&(metadata.num_qubits as u32).to_le_bytes());
819 compressed.extend_from_slice(&metadata.sparsity_ratio.to_le_bytes());
820
821 if metadata.sparsity_ratio > 0.5 {
823 let complex_data = self.deserialize_matrix_from_bytes(data)?;
824 let _n = metadata.matrix_dims.0;
825
826 let mut non_zero_count = 0u32;
828 let mut non_zero_data = Vec::new();
829
830 for (idx, &complex) in complex_data.iter().enumerate() {
831 if complex.norm() >= self.config.tolerance {
832 non_zero_data.extend_from_slice(&(idx as u32).to_le_bytes());
833 non_zero_data.extend_from_slice(&complex.re.to_le_bytes());
834 non_zero_data.extend_from_slice(&complex.im.to_le_bytes());
835 non_zero_count += 1;
836 }
837 }
838
839 compressed.extend_from_slice(&non_zero_count.to_le_bytes());
840 compressed.extend_from_slice(&non_zero_data);
841 } else {
842 compressed.extend_from_slice(data);
844 }
845
846 Ok(compressed)
847 }
848
849 fn deserialize_matrix_from_bytes(&self, bytes: &[u8]) -> QuantRS2Result<Vec<Complex64>> {
851 if bytes.len() % 16 != 0 {
852 return Err(QuantRS2Error::InvalidInput(
853 "Invalid byte length for Complex64 array".to_string(),
854 ));
855 }
856
857 let mut matrix = Vec::with_capacity(bytes.len() / 16);
858
859 for chunk in bytes.chunks_exact(16) {
860 let re = f64::from_le_bytes([
861 chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
862 ]);
863 let im = f64::from_le_bytes([
864 chunk[8], chunk[9], chunk[10], chunk[11], chunk[12], chunk[13], chunk[14],
865 chunk[15],
866 ]);
867
868 matrix.push(Complex64::new(re, im));
869 }
870
871 Ok(matrix)
872 }
873
874 pub fn decompress_gate(&self, compressed: &CompressedGate) -> QuantRS2Result<Box<dyn GateOp>> {
876 match compressed {
877 CompressedGate::RuntimeCompressed {
878 compressed_data,
879 compression_type,
880 original_size,
881 gate_metadata,
882 } => {
883 let decompressed_bytes = self.decompress_data(
885 compressed_data,
886 *compression_type,
887 *original_size,
888 gate_metadata,
889 )?;
890
891 let matrix = self.deserialize_matrix_from_bytes(&decompressed_bytes)?;
893
894 let n = gate_metadata.matrix_dims.0;
896 let mut matrix_2d = Array2::zeros((n, n));
897 for j in 0..n {
898 for i in 0..n {
899 matrix_2d[(i, j)] = matrix[j * n + i];
900 }
901 }
902
903 Ok(Box::new(CustomGate::with_qubits(
904 gate_metadata.name.clone(),
905 matrix_2d,
906 gate_metadata.qubits.clone(),
907 )))
908 }
909 CompressedGate::LowRank { left, right, .. } => {
910 let reconstructed = left.dot(&right.t());
912 Ok(Box::new(CustomGate::new(
913 "LowRank".to_string(),
914 reconstructed,
915 )))
916 }
917 CompressedGate::Tucker { core, factors: _ } => {
918 Ok(Box::new(CustomGate::new(
921 "Tucker".to_string(),
922 core.clone(),
923 )))
924 }
925 CompressedGate::Parameterized {
926 gate_type,
927 parameters,
928 qubits,
929 } => {
930 self.reconstruct_parameterized_gate(gate_type, parameters, qubits)
932 }
933 CompressedGate::Original(gate) => Ok(gate.clone_gate()),
934 }
935 }
936
937 fn decompress_data(
939 &self,
940 compressed_data: &[u8],
941 compression_type: CompressionType,
942 original_size: usize,
943 metadata: &GateMetadata,
944 ) -> QuantRS2Result<Vec<u8>> {
945 match compression_type {
946 CompressionType::None => Ok(compressed_data.to_vec()),
947 CompressionType::Zlib => self.decompress_zlib(compressed_data),
948 CompressionType::LZ4 => self.decompress_lz4(compressed_data, original_size),
949 CompressionType::QuantumOptimized => {
950 self.decompress_quantum_optimized(compressed_data, metadata)
951 }
952 CompressionType::Huffman => {
953 Ok(compressed_data.to_vec())
955 }
956 }
957 }
958
959 fn decompress_zlib(&self, compressed_data: &[u8]) -> QuantRS2Result<Vec<u8>> {
961 #[cfg(feature = "compression")]
962 {
963 use std::io::Read;
964
965 let mut decoder = flate2::read::ZlibDecoder::new(compressed_data);
966 let mut decompressed = Vec::new();
967
968 decoder.read_to_end(&mut decompressed).map_err(|e| {
969 QuantRS2Error::RuntimeError(format!("Zlib decompression failed: {}", e))
970 })?;
971
972 Ok(decompressed)
973 }
974
975 #[cfg(not(feature = "compression"))]
976 {
977 Ok(compressed_data.to_vec())
979 }
980 }
981
982 fn decompress_lz4(
984 &self,
985 compressed_data: &[u8],
986 original_size: usize,
987 ) -> QuantRS2Result<Vec<u8>> {
988 let mut decompressed = Vec::with_capacity(original_size);
990 let mut i = 0;
991
992 while i < compressed_data.len() {
993 if compressed_data[i] == 0xFF && i + 2 < compressed_data.len() {
994 let count = compressed_data[i + 1] as usize;
996 let byte = compressed_data[i + 2];
997
998 for _ in 0..count {
999 decompressed.push(byte);
1000 }
1001
1002 i += 3;
1003 } else {
1004 decompressed.push(compressed_data[i]);
1006 i += 1;
1007 }
1008 }
1009
1010 Ok(decompressed)
1011 }
1012
1013 fn decompress_quantum_optimized(
1015 &self,
1016 compressed_data: &[u8],
1017 metadata: &GateMetadata,
1018 ) -> QuantRS2Result<Vec<u8>> {
1019 if compressed_data.len() < 12 {
1020 return Err(QuantRS2Error::InvalidInput(
1021 "Invalid quantum-optimized compressed data".to_string(),
1022 ));
1023 }
1024
1025 let mut cursor = 0;
1026
1027 let _num_qubits = u32::from_le_bytes([
1029 compressed_data[cursor],
1030 compressed_data[cursor + 1],
1031 compressed_data[cursor + 2],
1032 compressed_data[cursor + 3],
1033 ]);
1034 cursor += 4;
1035
1036 let sparsity_ratio = f64::from_le_bytes([
1037 compressed_data[cursor],
1038 compressed_data[cursor + 1],
1039 compressed_data[cursor + 2],
1040 compressed_data[cursor + 3],
1041 compressed_data[cursor + 4],
1042 compressed_data[cursor + 5],
1043 compressed_data[cursor + 6],
1044 compressed_data[cursor + 7],
1045 ]);
1046 cursor += 8;
1047
1048 if sparsity_ratio > 0.5 {
1049 let non_zero_count = u32::from_le_bytes([
1051 compressed_data[cursor],
1052 compressed_data[cursor + 1],
1053 compressed_data[cursor + 2],
1054 compressed_data[cursor + 3],
1055 ]);
1056 cursor += 4;
1057
1058 let matrix_size = metadata.matrix_dims.0 * metadata.matrix_dims.1;
1059 let mut matrix = vec![Complex64::new(0.0, 0.0); matrix_size];
1060
1061 for _ in 0..non_zero_count {
1062 let index = u32::from_le_bytes([
1063 compressed_data[cursor],
1064 compressed_data[cursor + 1],
1065 compressed_data[cursor + 2],
1066 compressed_data[cursor + 3],
1067 ]) as usize;
1068 cursor += 4;
1069
1070 let re = f64::from_le_bytes([
1071 compressed_data[cursor],
1072 compressed_data[cursor + 1],
1073 compressed_data[cursor + 2],
1074 compressed_data[cursor + 3],
1075 compressed_data[cursor + 4],
1076 compressed_data[cursor + 5],
1077 compressed_data[cursor + 6],
1078 compressed_data[cursor + 7],
1079 ]);
1080 cursor += 8;
1081
1082 let im = f64::from_le_bytes([
1083 compressed_data[cursor],
1084 compressed_data[cursor + 1],
1085 compressed_data[cursor + 2],
1086 compressed_data[cursor + 3],
1087 compressed_data[cursor + 4],
1088 compressed_data[cursor + 5],
1089 compressed_data[cursor + 6],
1090 compressed_data[cursor + 7],
1091 ]);
1092 cursor += 8;
1093
1094 if index < matrix_size {
1095 matrix[index] = Complex64::new(re, im);
1096 }
1097 }
1098
1099 self.serialize_matrix(&matrix)
1101 } else {
1102 Ok(compressed_data[cursor..].to_vec())
1104 }
1105 }
1106
1107 fn reconstruct_parameterized_gate(
1109 &self,
1110 gate_type: &str,
1111 parameters: &[f64],
1112 qubits: &[QubitId],
1113 ) -> QuantRS2Result<Box<dyn GateOp>> {
1114 match gate_type {
1115 "rotation" => {
1116 if parameters.len() >= 3 && !qubits.is_empty() {
1117 let matrix = self.rotation_matrix_from_params(parameters, 2);
1119 Ok(Box::new(CustomGate::with_qubits(
1120 "Rotation".to_string(),
1121 matrix,
1122 qubits.to_vec(),
1123 )))
1124 } else {
1125 Err(QuantRS2Error::InvalidInput(
1126 "Invalid rotation parameters".to_string(),
1127 ))
1128 }
1129 }
1130 "phase" => {
1131 if !parameters.is_empty() && !qubits.is_empty() {
1132 let matrix = self.phase_matrix_from_params(parameters, 2);
1133 Ok(Box::new(CustomGate::with_qubits(
1134 "Phase".to_string(),
1135 matrix,
1136 qubits.to_vec(),
1137 )))
1138 } else {
1139 Err(QuantRS2Error::InvalidInput(
1140 "Invalid phase parameters".to_string(),
1141 ))
1142 }
1143 }
1144 _ => {
1145 let dim = 1 << qubits.len();
1147 let matrix = Array2::eye(dim);
1148 Ok(Box::new(CustomGate::with_qubits(
1149 gate_type.to_string(),
1150 matrix,
1151 qubits.to_vec(),
1152 )))
1153 }
1154 }
1155 }
1156}
1157
1158struct CompressionResult {
1160 data: Vec<u8>,
1161 compression_type: CompressionType,
1162 compression_ratio: f64,
1163}
1164
1165#[derive(Debug, Clone)]
1167pub struct CustomGate {
1168 name: String,
1169 matrix: Array2<Complex64>,
1170 qubits: Vec<QubitId>,
1171}
1172
1173impl CustomGate {
1174 pub fn new(name: String, matrix: Array2<Complex64>) -> Self {
1175 let n_qubits = (matrix.dim().0 as f64).log2() as usize;
1177 let qubits = (0..n_qubits).map(|i| QubitId::new(i as u32)).collect();
1178 Self {
1179 name,
1180 matrix,
1181 qubits,
1182 }
1183 }
1184
1185 pub fn with_qubits(name: String, matrix: Array2<Complex64>, qubits: Vec<QubitId>) -> Self {
1186 Self {
1187 name,
1188 matrix,
1189 qubits,
1190 }
1191 }
1192}
1193
1194impl GateOp for CustomGate {
1195 fn name(&self) -> &'static str {
1196 Box::leak(self.name.clone().into_boxed_str())
1198 }
1199
1200 fn qubits(&self) -> Vec<QubitId> {
1201 self.qubits.clone()
1202 }
1203
1204 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
1205 let mut result = Vec::with_capacity(self.matrix.len());
1207 let (rows, cols) = self.matrix.dim();
1208 for j in 0..cols {
1209 for i in 0..rows {
1210 result.push(self.matrix[(i, j)]);
1211 }
1212 }
1213 Ok(result)
1214 }
1215
1216 fn as_any(&self) -> &dyn Any {
1217 self
1218 }
1219
1220 fn clone_gate(&self) -> Box<dyn GateOp> {
1221 Box::new(self.clone())
1222 }
1223}
1224
1225#[derive(Debug, Clone, Default)]
1227pub struct CompressionStats {
1228 pub original_gates: usize,
1229 pub compressed_gates: usize,
1230 pub low_rank_compressions: usize,
1231 pub tucker_compressions: usize,
1232 pub parameterized_compressions: usize,
1233 pub compression_ratio: f64,
1234 pub total_parameters_before: usize,
1235 pub total_parameters_after: usize,
1236}
1237
1238impl GateSequenceCompressor {
1239 pub fn get_stats(
1241 &self,
1242 original: &[Box<dyn GateOp>],
1243 compressed: &[CompressedGate],
1244 ) -> CompressionStats {
1245 let mut stats = CompressionStats::default();
1246 stats.original_gates = original.len();
1247 stats.compressed_gates = compressed.len();
1248
1249 for gate in compressed {
1250 match gate {
1251 CompressedGate::LowRank { left, right, .. } => {
1252 stats.low_rank_compressions += 1;
1253 stats.total_parameters_after += (left.len() + right.len()) * 2;
1254 }
1255 CompressedGate::Tucker { core, factors } => {
1256 stats.tucker_compressions += 1;
1257 stats.total_parameters_after += core.len() * 2;
1258 stats.total_parameters_after +=
1259 factors.iter().map(|f| f.len() * 2).sum::<usize>();
1260 }
1261 CompressedGate::Parameterized { parameters, .. } => {
1262 stats.parameterized_compressions += 1;
1263 stats.total_parameters_after += parameters.len();
1264 }
1265 CompressedGate::RuntimeCompressed {
1266 compressed_data, ..
1267 } => {
1268 stats.total_parameters_after += compressed_data.len();
1270 }
1271 CompressedGate::Original(gate) => {
1272 if let Ok(matrix_vec) = gate.matrix() {
1273 let size = (matrix_vec.len() as f64).sqrt() as usize;
1274 stats.total_parameters_after += size * size * 2;
1275 }
1276 }
1277 }
1278 }
1279
1280 for gate in original {
1281 if let Ok(matrix_vec) = gate.matrix() {
1282 let size = (matrix_vec.len() as f64).sqrt() as usize;
1283 stats.total_parameters_before += size * size * 2;
1284 }
1285 }
1286
1287 stats.compression_ratio = if stats.total_parameters_before > 0 {
1288 stats.total_parameters_after as f64 / stats.total_parameters_before as f64
1289 } else {
1290 1.0
1291 };
1292
1293 stats
1294 }
1295}
1296
1297#[cfg(test)]
1298mod tests {
1299 use super::*;
1300 use crate::gate::single::{Hadamard, PauliX, PauliZ};
1301 use crate::qubit::QubitId;
1302
1303 #[test]
1304 fn test_gate_compression() {
1305 let config = CompressionConfig::default();
1306 let mut compressor = GateSequenceCompressor::new(config);
1307
1308 let h_gate = Hadamard {
1310 target: QubitId::new(0),
1311 };
1312 let compressed = compressor.compress_gate(&h_gate).unwrap();
1313
1314 match compressed {
1315 CompressedGate::Original(_) => {
1316 }
1318 CompressedGate::RuntimeCompressed { .. } => {
1319 }
1321 _ => panic!("H gate shouldn't be significantly compressed"),
1322 }
1323 }
1324
1325 #[test]
1326 fn test_sequence_compression() {
1327 let config = CompressionConfig::default();
1328 let mut compressor = GateSequenceCompressor::new(config);
1329
1330 let gates: Vec<Box<dyn GateOp>> = vec![
1332 Box::new(Hadamard {
1333 target: QubitId::new(0),
1334 }),
1335 Box::new(PauliX {
1336 target: QubitId::new(0),
1337 }),
1338 Box::new(Hadamard {
1339 target: QubitId::new(0),
1340 }),
1341 ];
1342
1343 let compressed = compressor.compress_sequence(&gates).unwrap();
1344 assert!(compressed.len() <= gates.len());
1345 }
1346
1347 #[test]
1348 fn test_compression_stats() {
1349 let config = CompressionConfig::default();
1350 let mut compressor = GateSequenceCompressor::new(config);
1351
1352 let gates: Vec<Box<dyn GateOp>> = vec![
1353 Box::new(Hadamard {
1354 target: QubitId::new(0),
1355 }),
1356 Box::new(PauliZ {
1357 target: QubitId::new(0),
1358 }),
1359 ];
1360
1361 let compressed = compressor.compress_sequence(&gates).unwrap();
1362 let stats = compressor.get_stats(&gates, &compressed);
1363
1364 assert_eq!(stats.original_gates, 2);
1365 assert!(stats.compression_ratio >= 0.0);
1367 }
1368}