1use super::access_tracking::{AccessEvent, AccessPattern, AccessTracker, AccessType};
7use super::cache::{BlockCache, BlockId};
8use super::compressed_data::{BlockType, CompressedBlock, CompressedMatrix};
9use super::compression::{CompressionEngine, CompressionResult};
10use super::config::{AdaptiveCompressionConfig, CompressionAlgorithm};
11use super::memory_mapping::{MemoryMappingConfig, MemoryMappingManager};
12use super::out_of_core::OutOfCoreManager;
13use super::stats::AccessPatternType;
14use super::stats::{CompressionMetadata, CompressionStats, MemoryStats};
15use crate::error::{SparseError, SparseResult};
16use scirs2_core::numeric::{Float, NumAssign, SparseElement};
17use std::marker::PhantomData;
18use std::sync::atomic::{AtomicUsize, Ordering};
19use std::sync::{Arc, Mutex};
20
21pub struct AdaptiveMemoryCompressor {
23 config: AdaptiveCompressionConfig,
24 memory_usage: AtomicUsize,
25 compression_stats: Arc<Mutex<CompressionStats>>,
26 block_cache: Arc<Mutex<BlockCache>>,
27 access_tracker: Arc<Mutex<AccessTracker>>,
28 compression_engine: CompressionEngine,
29 hierarchical_levels: Vec<CompressionLevel>,
30 out_of_core_manager: Option<OutOfCoreManager>,
31 memory_mapping_manager: Option<MemoryMappingManager>,
32}
33
34#[derive(Debug, Clone)]
36struct CompressionLevel {
37 level: u8,
38 compression_ratio: f64,
39 algorithm: CompressionAlgorithm,
40 block_size: usize,
41 access_threshold: usize,
42}
43
44#[derive(Debug)]
46struct CompressionStrategy {
47 algorithm: CompressionAlgorithm,
48 block_size: usize,
49 hierarchical: bool,
50 predicted_ratio: f64,
51}
52
53#[derive(Debug, Default)]
55struct SparsityPatternAnalysis {
56 avg_nnz_per_row: f64,
57 max_nnz_per_row: usize,
58 min_nnz_per_row: usize,
59 sequential_patterns: usize,
60 clustering_factor: f64,
61 bandwidth: usize,
62}
63
64#[derive(Debug, Default)]
66struct AccessPatternInfo {
67 total_accesses: usize,
68 avg_temporal_locality: f64,
69 avg_spatial_locality: f64,
70 pattern_count: usize,
71}
72
73#[derive(Debug)]
75struct CacheStats {
76 hits: usize,
77 misses: usize,
78 hit_ratio: f64,
79}
80
81impl AdaptiveMemoryCompressor {
82 pub fn new(config: AdaptiveCompressionConfig) -> SparseResult<Self> {
84 let block_cache = BlockCache::new(config.cache_size);
85 let access_tracker = AccessTracker::new(); let compression_engine = CompressionEngine::new();
87
88 let hierarchical_levels = vec![
90 CompressionLevel {
91 level: 1,
92 compression_ratio: 2.0,
93 algorithm: CompressionAlgorithm::RLE,
94 block_size: config.block_size,
95 access_threshold: 100,
96 },
97 CompressionLevel {
98 level: 2,
99 compression_ratio: 4.0,
100 algorithm: CompressionAlgorithm::Delta,
101 block_size: config.block_size / 2,
102 access_threshold: 50,
103 },
104 CompressionLevel {
105 level: 3,
106 compression_ratio: 8.0,
107 algorithm: CompressionAlgorithm::LZ77,
108 block_size: config.block_size / 4,
109 access_threshold: 10,
110 },
111 ];
112
113 let out_of_core_manager = if config.out_of_core {
115 Some(OutOfCoreManager::new(&config.temp_directory)?)
116 } else {
117 None
118 };
119
120 let memory_mapping_manager = if config.memory_mapping {
122 let mapping_config = MemoryMappingConfig {
123 read_only: false,
124 write_through: true,
125 prefetch: true,
126 page_size_hint: 4096,
127 };
128 Some(MemoryMappingManager::new(mapping_config))
129 } else {
130 None
131 };
132
133 Ok(Self {
134 config,
135 memory_usage: AtomicUsize::new(0),
136 compression_stats: Arc::new(Mutex::new(CompressionStats::new())),
137 block_cache: Arc::new(Mutex::new(block_cache)),
138 access_tracker: Arc::new(Mutex::new(access_tracker)),
139 compression_engine,
140 hierarchical_levels,
141 out_of_core_manager,
142 memory_mapping_manager,
143 })
144 }
145
146 #[allow(clippy::too_many_arguments)]
148 pub fn compress_matrix<T>(
149 &mut self,
150 matrix_id: u64,
151 rows: usize,
152 indptr: &[usize],
153 indices: &[usize],
154 data: &[T],
155 ) -> SparseResult<CompressedMatrix<T>>
156 where
157 T: Float + SparseElement + NumAssign + Send + Sync + Copy + std::fmt::Debug,
158 {
159 let total_size = std::mem::size_of_val(indptr)
160 + std::mem::size_of_val(indices)
161 + std::mem::size_of_val(data);
162
163 let current_usage = self.memory_usage.load(Ordering::Relaxed);
165 let usage_ratio = (current_usage + total_size) as f64 / self.config.memory_budget as f64;
166
167 if usage_ratio < self.config.compression_threshold && !self.config.adaptive_compression {
168 return self.create_uncompressed_matrix(matrix_id, rows, indptr, indices, data);
170 }
171
172 let start_time = std::time::Instant::now();
173
174 let compression_strategy =
176 self.determine_compression_strategy(matrix_id, rows, indptr, indices)?;
177
178 let compressed_blocks = self.apply_compression_strategy(
180 &compression_strategy,
181 matrix_id,
182 rows,
183 indptr,
184 indices,
185 data,
186 )?;
187
188 let compression_time = start_time.elapsed().as_secs_f64();
189
190 self.update_compression_stats(total_size, &compressed_blocks, compression_time);
192
193 let compressed_size = compressed_blocks
195 .iter()
196 .map(|b| b.compressed_data.len())
197 .sum::<usize>();
198 self.memory_usage
199 .fetch_add(compressed_size, Ordering::Relaxed);
200
201 self.handle_out_of_core_storage(&compressed_blocks)?;
203
204 let mut compressed_matrix = CompressedMatrix::new(
205 matrix_id,
206 rows,
207 if !indptr.is_empty() {
208 *indices.iter().max().unwrap_or(&0) + 1
209 } else {
210 0
211 },
212 compression_strategy.algorithm,
213 compression_strategy.block_size,
214 );
215
216 for block in compressed_blocks {
218 compressed_matrix.add_block(block);
219 }
220
221 Ok(compressed_matrix)
222 }
223
224 pub fn decompress_matrix<T>(
226 &mut self,
227 compressed_matrix: &CompressedMatrix<T>,
228 ) -> SparseResult<(Vec<usize>, Vec<usize>, Vec<T>)>
229 where
230 T: Float
231 + SparseElement
232 + NumAssign
233 + Send
234 + Sync
235 + Copy
236 + std::fmt::Debug
237 + scirs2_core::numeric::FromPrimitive,
238 {
239 let start_time = std::time::Instant::now();
240
241 let mut indptr = Vec::new();
242 let mut indices = Vec::new();
243 let mut data = Vec::new();
244
245 for block in compressed_matrix.get_blocks_row_major() {
247 let decompressed_data =
249 if let Some(cached_data) = self.get_cached_block(&block.blockid)? {
250 cached_data
251 } else {
252 let decompressed =
254 self.decompress_block(block, compressed_matrix.compression_algorithm)?;
255 self.cache_block(&block.blockid, &decompressed)?;
256 decompressed
257 };
258
259 match block.block_type {
261 BlockType::IndPtr => {
262 indptr.extend(self.parse_indptr_data(&decompressed_data)?);
263 }
264 BlockType::Indices => {
265 indices.extend(self.parse_indices_data(&decompressed_data)?);
266 }
267 BlockType::Data => {
268 data.extend(self.parse_data_values::<T>(&decompressed_data)?);
269 }
270 BlockType::Combined => {
271 let (block_indptr, block_indices, block_data) =
272 self.parse_combined_data::<T>(&decompressed_data)?;
273 indptr.extend(block_indptr);
274 indices.extend(block_indices);
275 data.extend(block_data);
276 }
277 BlockType::Metadata => {
278 }
280 }
281 }
282
283 let decompression_time = start_time.elapsed().as_secs_f64();
284
285 if let Ok(mut stats) = self.compression_stats.lock() {
287 stats.decompression_time += decompression_time;
288 }
289
290 self.record_matrix_access(compressed_matrix.matrixid, AccessType::Read);
292
293 Ok((indptr, indices, data))
294 }
295
296 pub fn get_memory_stats(&self) -> MemoryStats {
298 let current_usage = self.memory_usage.load(Ordering::Relaxed);
299 let usage_ratio = current_usage as f64 / self.config.memory_budget as f64;
300
301 let compression_stats = self
302 .compression_stats
303 .lock()
304 .expect("Operation failed")
305 .clone();
306 let cache_stats = self.get_cache_stats();
307
308 let mut memory_stats = MemoryStats::new(self.config.memory_budget, self.config.out_of_core);
309 memory_stats.update_memory_usage(current_usage);
310 memory_stats.compression_stats = compression_stats;
311 memory_stats.cache_hits = cache_stats.hits;
312 memory_stats.cache_misses = cache_stats.misses;
313 memory_stats.cache_hit_ratio = cache_stats.hit_ratio;
314 memory_stats
315 }
316
317 pub fn get_stats(&self) -> CompressionStats {
319 self.compression_stats
320 .lock()
321 .expect("Operation failed")
322 .clone()
323 }
324
325 pub fn optimize_for_sequential_access(&mut self) {
327 }
330
331 pub fn optimize_for_random_access(&mut self) {
333 }
336
337 pub fn reset(&mut self) -> SparseResult<()> {
339 if let Ok(mut cache) = self.block_cache.lock() {
341 cache.clear();
342 }
343
344 if let Ok(mut stats) = self.compression_stats.lock() {
346 *stats = CompressionStats::new();
347 }
348
349 if let Ok(mut tracker) = self.access_tracker.lock() {
351 tracker.cleanup_old_patterns(0); }
353
354 self.memory_usage.store(0, Ordering::Relaxed);
356
357 if let Some(ref mut manager) = self.out_of_core_manager {
359 manager.cleanup()?;
360 }
361
362 Ok(())
363 }
364
365 fn determine_compression_strategy(
368 &self,
369 matrix_id: u64,
370 rows: usize,
371 indptr: &[usize],
372 indices: &[usize],
373 ) -> SparseResult<CompressionStrategy> {
374 let nnz = indices.len();
376 let density = if rows > 0 && !indices.is_empty() {
377 let max_col = *indices.iter().max().unwrap_or(&0);
378 nnz as f64 / (rows as f64 * (max_col + 1) as f64)
379 } else {
380 0.0
381 };
382
383 let pattern_analysis = self.analyze_sparsity_patterns(indptr, indices);
385
386 let access_info = self.get_access_pattern_info(matrix_id);
388
389 let algorithm = if self.config.adaptive_compression {
391 self.select_adaptive_algorithm(density, &pattern_analysis, &access_info)
392 } else {
393 self.config.compression_algorithm
394 };
395
396 let block_size = self.determine_optimal_block_size(rows, nnz, density);
398
399 Ok(CompressionStrategy {
400 algorithm,
401 block_size,
402 hierarchical: self.config.hierarchical_compression,
403 predicted_ratio: self.predict_compression_ratio(algorithm, density, &pattern_analysis),
404 })
405 }
406
407 fn apply_compression_strategy<T>(
408 &mut self,
409 strategy: &CompressionStrategy,
410 matrix_id: u64,
411 rows: usize,
412 indptr: &[usize],
413 indices: &[usize],
414 data: &[T],
415 ) -> SparseResult<Vec<CompressedBlock>>
416 where
417 T: Float + SparseElement + NumAssign + Send + Sync + Copy + std::fmt::Debug,
418 {
419 let indptr_data = self.serialize_indptr(indptr)?;
421 let indices_data = self.serialize_indices(indices)?;
422 let data_data = self.serialize_data(data)?;
423
424 let mut blocks = Vec::new();
425
426 let indptr_block_id = BlockId::new(matrix_id, 0, 0);
428 let indptr_result = self.compression_engine.compress(
429 &indptr_data,
430 strategy.algorithm,
431 &indptr_block_id,
432 BlockType::IndPtr,
433 )?;
434 blocks.push(CompressedBlock::new(
435 indptr_block_id,
436 BlockType::IndPtr,
437 indptr_result.compressed_data,
438 indptr_data.len(),
439 (indptr_result.compression_ratio.clamp(1.0, 10.0) as u8).max(1),
440 ));
441
442 let indices_block_id = BlockId::new(matrix_id, 0, 1);
444 let indices_result = self.compression_engine.compress(
445 &indices_data,
446 strategy.algorithm,
447 &indices_block_id,
448 BlockType::Indices,
449 )?;
450 blocks.push(CompressedBlock::new(
451 indices_block_id,
452 BlockType::Indices,
453 indices_result.compressed_data,
454 indices_data.len(),
455 (indices_result.compression_ratio.clamp(1.0, 10.0) as u8).max(1),
456 ));
457
458 let data_block_id = BlockId::new(matrix_id, 0, 2);
460 let data_result = self.compression_engine.compress(
461 &data_data,
462 strategy.algorithm,
463 &data_block_id,
464 BlockType::Data,
465 )?;
466 blocks.push(CompressedBlock::new(
467 data_block_id,
468 BlockType::Data,
469 data_result.compressed_data,
470 data_data.len(),
471 (data_result.compression_ratio.clamp(1.0, 10.0) as u8).max(1),
472 ));
473
474 Ok(blocks)
475 }
476
477 fn analyze_sparsity_patterns(
478 &self,
479 indptr: &[usize],
480 indices: &[usize],
481 ) -> SparsityPatternAnalysis {
482 let mut analysis = SparsityPatternAnalysis::default();
483
484 if indptr.len() <= 1 {
485 return analysis;
486 }
487
488 let rows = indptr.len() - 1;
489
490 let mut row_nnz = Vec::new();
492 for row in 0..rows {
493 row_nnz.push(indptr[row + 1] - indptr[row]);
494 }
495
496 analysis.avg_nnz_per_row = row_nnz.iter().sum::<usize>() as f64 / rows as f64;
497 analysis.max_nnz_per_row = *row_nnz.iter().max().unwrap_or(&0);
498 analysis.min_nnz_per_row = *row_nnz.iter().min().unwrap_or(&0);
499
500 analysis.sequential_patterns = self.count_sequential_patterns(indices);
502 analysis.clustering_factor = self.calculate_clustering_factor(indptr, indices);
503 analysis.bandwidth = self.calculate_bandwidth(indptr, indices);
504
505 analysis
506 }
507
508 fn count_sequential_patterns(&self, indices: &[usize]) -> usize {
509 let mut sequential_count = 0;
510 let mut current_sequence = 0;
511
512 for window in indices.windows(2) {
513 if window[1] == window[0] + 1 {
514 current_sequence += 1;
515 } else {
516 if current_sequence >= 3 {
517 sequential_count += current_sequence;
518 }
519 current_sequence = 0;
520 }
521 }
522
523 if current_sequence >= 3 {
524 sequential_count += current_sequence;
525 }
526
527 sequential_count
528 }
529
530 fn calculate_clustering_factor(&self, indptr: &[usize], indices: &[usize]) -> f64 {
531 if indptr.len() <= 1 {
532 return 0.0;
533 }
534
535 let mut total_distance = 0.0;
536 let mut total_pairs = 0;
537
538 let rows = indptr.len() - 1;
539 for row in 0..rows {
540 let start = indptr[row];
541 let end = indptr[row + 1];
542
543 if end > start + 1 {
544 for i in start..(end - 1) {
545 total_distance += (indices[i + 1] - indices[i]) as f64;
546 total_pairs += 1;
547 }
548 }
549 }
550
551 if total_pairs > 0 {
552 total_distance / total_pairs as f64
553 } else {
554 0.0
555 }
556 }
557
558 fn calculate_bandwidth(&self, indptr: &[usize], indices: &[usize]) -> usize {
559 if indptr.len() <= 1 {
560 return 0;
561 }
562
563 let mut max_bandwidth = 0;
564 let rows = indptr.len() - 1;
565
566 for row in 0..rows {
567 let start = indptr[row];
568 let end = indptr[row + 1];
569
570 if end > start {
571 let min_col = indices[start];
572 let max_col = indices[end - 1];
573 let bandwidth = max_col.saturating_sub(min_col);
574 max_bandwidth = max_bandwidth.max(bandwidth);
575 }
576 }
577
578 max_bandwidth
579 }
580
581 fn get_access_pattern_info(&self, _matrix_id: u64) -> AccessPatternInfo {
582 let access_tracker = self.access_tracker.lock().expect("Operation failed");
583 let stats = access_tracker.get_statistics();
585 AccessPatternInfo {
586 total_accesses: stats.total_access_events,
587 avg_temporal_locality: 0.5, avg_spatial_locality: 0.5, pattern_count: stats.total_tracked_blocks,
590 }
591 }
592
593 fn select_adaptive_algorithm(
594 &self,
595 density: f64,
596 pattern_analysis: &SparsityPatternAnalysis,
597 access_info: &AccessPatternInfo,
598 ) -> CompressionAlgorithm {
599 if density > 0.1 {
601 CompressionAlgorithm::LZ77
603 } else if pattern_analysis.sequential_patterns
604 > pattern_analysis.avg_nnz_per_row as usize * 10
605 {
606 CompressionAlgorithm::RLE
608 } else if pattern_analysis.clustering_factor < 2.0 {
609 CompressionAlgorithm::Delta
611 } else if access_info.avg_temporal_locality > 0.8 {
612 CompressionAlgorithm::SparseOptimized
614 } else {
615 CompressionAlgorithm::Huffman
617 }
618 }
619
620 fn determine_optimal_block_size(&self, rows: usize, nnz: usize, density: f64) -> usize {
621 let base_block_size = self.config.block_size;
622
623 let size_factor = if rows > 1_000_000 {
625 2.0 } else if rows < 10_000 {
627 0.5 } else {
629 1.0
630 };
631
632 let density_factor = if density > 0.1 {
633 1.5 } else {
635 1.0
636 };
637
638 let nnz_factor = if nnz > 10_000_000 {
639 1.5 } else {
641 1.0
642 };
643
644 let optimal_size =
645 (base_block_size as f64 * size_factor * density_factor * nnz_factor) as usize;
646 optimal_size.clamp(4096, 16 * 1024 * 1024) }
648
649 fn predict_compression_ratio(
650 &self,
651 algorithm: CompressionAlgorithm,
652 density: f64,
653 pattern_analysis: &SparsityPatternAnalysis,
654 ) -> f64 {
655 let base_ratio = algorithm.expected_compression_ratio();
656
657 let adjustment = if pattern_analysis.bandwidth > 100000 {
659 0.8 } else if pattern_analysis.bandwidth < 100 {
661 1.2 } else {
663 1.0
664 };
665
666 base_ratio * adjustment
667 }
668
669 fn handle_out_of_core_storage(&mut self, blocks: &[CompressedBlock]) -> SparseResult<()> {
670 if let Some(ref mut manager) = self.out_of_core_manager {
671 for block in blocks {
672 if block.compressed_data.len() > self.config.block_size {
673 manager.write_block_to_disk(block)?;
675 }
676 }
677 }
678 Ok(())
679 }
680
681 fn get_cached_block(&self, block_id: &BlockId) -> SparseResult<Option<Vec<u8>>> {
682 if let Ok(mut cache) = self.block_cache.lock() {
683 Ok(cache
684 .get(block_id)
685 .map(|cached_block| cached_block.data.clone()))
686 } else {
687 Ok(None)
688 }
689 }
690
691 fn cache_block(&self, block_id: &BlockId, data: &[u8]) -> SparseResult<()> {
692 use super::cache::CachedBlock;
693 if let Ok(mut cache) = self.block_cache.lock() {
694 let cached_block = CachedBlock::new(data.to_vec(), false, 1);
695 cache.insert(block_id.clone(), cached_block);
696 }
697 Ok(())
698 }
699
700 fn record_matrix_access(&self, matrix_id: u64, access_type: AccessType) {
701 if let Ok(mut tracker) = self.access_tracker.lock() {
702 let block_id = BlockId::new(matrix_id, 0, 0); tracker.record_access(block_id, access_type, 1024); }
705 }
706
707 fn decompress_block(
708 &mut self,
709 block: &CompressedBlock,
710 algorithm: CompressionAlgorithm,
711 ) -> SparseResult<Vec<u8>> {
712 self.compression_engine
713 .decompress(&block.compressed_data, algorithm, block.original_size)
714 }
715
716 fn create_uncompressed_matrix<T>(
717 &self,
718 matrix_id: u64,
719 rows: usize,
720 indptr: &[usize],
721 indices: &[usize],
722 data: &[T],
723 ) -> SparseResult<CompressedMatrix<T>>
724 where
725 T: Float + SparseElement + NumAssign + Send + Sync + Copy + std::fmt::Debug,
726 {
727 let mut matrix = CompressedMatrix::new(
728 matrix_id,
729 rows,
730 if !indptr.is_empty() {
731 *indices.iter().max().unwrap_or(&0) + 1
732 } else {
733 0
734 },
735 CompressionAlgorithm::None,
736 self.config.block_size,
737 );
738
739 let indptr_data = self.serialize_indptr(indptr)?;
741 let indices_data = self.serialize_indices(indices)?;
742 let data_data = self.serialize_data(data)?;
743
744 matrix.add_block(CompressedBlock::new(
745 BlockId::new(matrix_id, 0, 0),
746 BlockType::IndPtr,
747 indptr_data.clone(),
748 indptr_data.len(),
749 0,
750 ));
751
752 matrix.add_block(CompressedBlock::new(
753 BlockId::new(matrix_id, 0, 1),
754 BlockType::Indices,
755 indices_data.clone(),
756 indices_data.len(),
757 0,
758 ));
759
760 matrix.add_block(CompressedBlock::new(
761 BlockId::new(matrix_id, 0, 2),
762 BlockType::Data,
763 data_data.clone(),
764 data_data.len(),
765 0,
766 ));
767
768 Ok(matrix)
769 }
770
771 fn update_compression_stats(
772 &self,
773 original_size: usize,
774 blocks: &[CompressedBlock],
775 compression_time: f64,
776 ) {
777 if let Ok(mut stats) = self.compression_stats.lock() {
778 let compressed_size = blocks
779 .iter()
780 .map(|b| b.compressed_data.len())
781 .sum::<usize>();
782
783 stats.update_compression(original_size, compressed_size, compression_time);
784 }
785 }
786
787 fn get_cache_stats(&self) -> CacheStats {
788 if let Ok(cache) = self.block_cache.lock() {
789 let stats = cache.get_stats();
790 CacheStats {
791 hits: stats.hit_count,
792 misses: stats.miss_count,
793 hit_ratio: if stats.hit_count + stats.miss_count > 0 {
794 stats.hit_count as f64 / (stats.hit_count + stats.miss_count) as f64
795 } else {
796 0.0
797 },
798 }
799 } else {
800 CacheStats {
801 hits: 0,
802 misses: 0,
803 hit_ratio: 0.0,
804 }
805 }
806 }
807
808 fn serialize_indptr(&self, indptr: &[usize]) -> SparseResult<Vec<u8>> {
810 let mut serialized = Vec::new();
811 serialized.extend_from_slice(&indptr.len().to_le_bytes());
812 for &value in indptr {
813 serialized.extend_from_slice(&value.to_le_bytes());
814 }
815 Ok(serialized)
816 }
817
818 fn serialize_indices(&self, indices: &[usize]) -> SparseResult<Vec<u8>> {
819 let mut serialized = Vec::new();
820 serialized.extend_from_slice(&indices.len().to_le_bytes());
821 for &value in indices {
822 serialized.extend_from_slice(&value.to_le_bytes());
823 }
824 Ok(serialized)
825 }
826
827 fn serialize_data<T>(&self, data: &[T]) -> SparseResult<Vec<u8>>
828 where
829 T: Float + SparseElement + NumAssign + Send + Sync + Copy,
830 {
831 let mut serialized = Vec::new();
832 serialized.extend_from_slice(&data.len().to_le_bytes());
833 for &value in data {
834 let bytes = value.to_f64().unwrap_or(0.0).to_le_bytes();
835 serialized.extend_from_slice(&bytes);
836 }
837 Ok(serialized)
838 }
839
840 fn parse_indptr_data(&self, data: &[u8]) -> SparseResult<Vec<usize>> {
842 if data.len() < 8 {
843 return Ok(Vec::new());
844 }
845
846 let length = usize::from_le_bytes([
847 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
848 ]);
849 let mut indptr = Vec::with_capacity(length);
850
851 let mut offset = 8;
852 for _ in 0..length {
853 if offset + 8 <= data.len() {
854 let value = usize::from_le_bytes([
855 data[offset],
856 data[offset + 1],
857 data[offset + 2],
858 data[offset + 3],
859 data[offset + 4],
860 data[offset + 5],
861 data[offset + 6],
862 data[offset + 7],
863 ]);
864 indptr.push(value);
865 offset += 8;
866 }
867 }
868
869 Ok(indptr)
870 }
871
872 fn parse_indices_data(&self, data: &[u8]) -> SparseResult<Vec<usize>> {
873 self.parse_indptr_data(data) }
875
876 fn parse_data_values<T>(&self, data: &[u8]) -> SparseResult<Vec<T>>
877 where
878 T: Float
879 + SparseElement
880 + NumAssign
881 + Send
882 + Sync
883 + Copy
884 + scirs2_core::numeric::FromPrimitive,
885 {
886 if data.len() < 8 {
887 return Ok(Vec::new());
888 }
889
890 let length = usize::from_le_bytes([
891 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
892 ]);
893 let mut values = Vec::with_capacity(length);
894
895 let mut offset = 8;
896 for _ in 0..length {
897 if offset + 8 <= data.len() {
898 let value_f64 = f64::from_le_bytes([
899 data[offset],
900 data[offset + 1],
901 data[offset + 2],
902 data[offset + 3],
903 data[offset + 4],
904 data[offset + 5],
905 data[offset + 6],
906 data[offset + 7],
907 ]);
908 if let Some(value) = T::from_f64(value_f64) {
909 values.push(value);
910 }
911 offset += 8;
912 }
913 }
914
915 Ok(values)
916 }
917
918 fn parse_combined_data<T>(&self, _data: &[u8]) -> SparseResult<(Vec<usize>, Vec<usize>, Vec<T>)>
919 where
920 T: Float + SparseElement + NumAssign + Send + Sync + Copy,
921 {
922 Ok((Vec::new(), Vec::new(), Vec::new()))
924 }
925}
926
927#[cfg(test)]
928mod tests {
929 use super::*;
930 use tempfile::TempDir;
931
932 #[test]
933 fn test_compressor_creation() {
934 let config = AdaptiveCompressionConfig::default();
935 let compressor = AdaptiveMemoryCompressor::new(config);
936 assert!(compressor.is_ok());
937 }
938
939 #[test]
940 fn test_matrix_compression_roundtrip() {
941 let config = AdaptiveCompressionConfig {
942 compression_algorithm: CompressionAlgorithm::None,
943 out_of_core: false,
944 memory_mapping: false,
945 ..Default::default()
946 };
947 let mut compressor = AdaptiveMemoryCompressor::new(config).expect("Operation failed");
948
949 let indptr = vec![0, 2, 3];
950 let indices = vec![0, 1, 1];
951 let data = vec![1.0, 2.0, 3.0];
952
953 let compressed = compressor
954 .compress_matrix(1, 2, &indptr, &indices, &data)
955 .expect("Operation failed");
956
957 let (decompressed_indptr, decompressed_indices, decompressed_data) = compressor
958 .decompress_matrix(&compressed)
959 .expect("Operation failed");
960
961 assert_eq!(decompressed_indptr, indptr);
962 assert_eq!(decompressed_indices, indices);
963 assert_eq!(decompressed_data.len(), data.len());
964 }
965
966 #[test]
967 fn test_memory_stats() {
968 let config = AdaptiveCompressionConfig::default();
969 let compressor = AdaptiveMemoryCompressor::new(config).expect("Operation failed");
970 let stats = compressor.get_memory_stats();
971
972 assert_eq!(stats.current_memory_usage, 0);
973 assert!(stats.memory_usage_ratio >= 0.0);
974 }
975
976 #[test]
977 fn test_access_pattern_optimization() {
978 let config = AdaptiveCompressionConfig::default();
979 let mut compressor = AdaptiveMemoryCompressor::new(config).expect("Operation failed");
980
981 compressor.optimize_for_sequential_access();
983
984 compressor.optimize_for_random_access();
986
987 }
989
990 #[test]
991 fn test_compressor_reset() {
992 let temp_dir = TempDir::new().expect("Operation failed");
993 let config = AdaptiveCompressionConfig {
994 temp_directory: temp_dir
995 .path()
996 .to_str()
997 .expect("Operation failed")
998 .to_string(),
999 ..Default::default()
1000 };
1001 let mut compressor = AdaptiveMemoryCompressor::new(config).expect("Operation failed");
1002
1003 let indptr = vec![0, 1];
1005 let indices = vec![0];
1006 let data = vec![1.0];
1007
1008 let _ = compressor.compress_matrix(1, 1, &indptr, &indices, &data);
1009
1010 let result = compressor.reset();
1012 assert!(result.is_ok());
1013
1014 let stats = compressor.get_memory_stats();
1015 assert_eq!(stats.current_memory_usage, 0);
1016 }
1017}