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};
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 + 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 + NumAssign
232 + Send
233 + Sync
234 + Copy
235 + std::fmt::Debug
236 + scirs2_core::numeric::FromPrimitive,
237 {
238 let start_time = std::time::Instant::now();
239
240 let mut indptr = Vec::new();
241 let mut indices = Vec::new();
242 let mut data = Vec::new();
243
244 for block in compressed_matrix.get_blocks_row_major() {
246 let decompressed_data =
248 if let Some(cached_data) = self.get_cached_block(&block.blockid)? {
249 cached_data
250 } else {
251 let decompressed =
253 self.decompress_block(block, compressed_matrix.compression_algorithm)?;
254 self.cache_block(&block.blockid, &decompressed)?;
255 decompressed
256 };
257
258 match block.block_type {
260 BlockType::IndPtr => {
261 indptr.extend(self.parse_indptr_data(&decompressed_data)?);
262 }
263 BlockType::Indices => {
264 indices.extend(self.parse_indices_data(&decompressed_data)?);
265 }
266 BlockType::Data => {
267 data.extend(self.parse_data_values::<T>(&decompressed_data)?);
268 }
269 BlockType::Combined => {
270 let (block_indptr, block_indices, block_data) =
271 self.parse_combined_data::<T>(&decompressed_data)?;
272 indptr.extend(block_indptr);
273 indices.extend(block_indices);
274 data.extend(block_data);
275 }
276 BlockType::Metadata => {
277 }
279 }
280 }
281
282 let decompression_time = start_time.elapsed().as_secs_f64();
283
284 if let Ok(mut stats) = self.compression_stats.lock() {
286 stats.decompression_time += decompression_time;
287 }
288
289 self.record_matrix_access(compressed_matrix.matrixid, AccessType::Read);
291
292 Ok((indptr, indices, data))
293 }
294
295 pub fn get_memory_stats(&self) -> MemoryStats {
297 let current_usage = self.memory_usage.load(Ordering::Relaxed);
298 let usage_ratio = current_usage as f64 / self.config.memory_budget as f64;
299
300 let compression_stats = self.compression_stats.lock().unwrap().clone();
301 let cache_stats = self.get_cache_stats();
302
303 let mut memory_stats = MemoryStats::new(self.config.memory_budget, self.config.out_of_core);
304 memory_stats.update_memory_usage(current_usage);
305 memory_stats.compression_stats = compression_stats;
306 memory_stats.cache_hits = cache_stats.hits;
307 memory_stats.cache_misses = cache_stats.misses;
308 memory_stats.cache_hit_ratio = cache_stats.hit_ratio;
309 memory_stats
310 }
311
312 pub fn get_stats(&self) -> CompressionStats {
314 self.compression_stats.lock().unwrap().clone()
315 }
316
317 pub fn optimize_for_sequential_access(&mut self) {
319 }
322
323 pub fn optimize_for_random_access(&mut self) {
325 }
328
329 pub fn reset(&mut self) -> SparseResult<()> {
331 if let Ok(mut cache) = self.block_cache.lock() {
333 cache.clear();
334 }
335
336 if let Ok(mut stats) = self.compression_stats.lock() {
338 *stats = CompressionStats::new();
339 }
340
341 if let Ok(mut tracker) = self.access_tracker.lock() {
343 tracker.cleanup_old_patterns(0); }
345
346 self.memory_usage.store(0, Ordering::Relaxed);
348
349 if let Some(ref mut manager) = self.out_of_core_manager {
351 manager.cleanup()?;
352 }
353
354 Ok(())
355 }
356
357 fn determine_compression_strategy(
360 &self,
361 matrix_id: u64,
362 rows: usize,
363 indptr: &[usize],
364 indices: &[usize],
365 ) -> SparseResult<CompressionStrategy> {
366 let nnz = indices.len();
368 let density = if rows > 0 && !indices.is_empty() {
369 let max_col = *indices.iter().max().unwrap_or(&0);
370 nnz as f64 / (rows as f64 * (max_col + 1) as f64)
371 } else {
372 0.0
373 };
374
375 let pattern_analysis = self.analyze_sparsity_patterns(indptr, indices);
377
378 let access_info = self.get_access_pattern_info(matrix_id);
380
381 let algorithm = if self.config.adaptive_compression {
383 self.select_adaptive_algorithm(density, &pattern_analysis, &access_info)
384 } else {
385 self.config.compression_algorithm
386 };
387
388 let block_size = self.determine_optimal_block_size(rows, nnz, density);
390
391 Ok(CompressionStrategy {
392 algorithm,
393 block_size,
394 hierarchical: self.config.hierarchical_compression,
395 predicted_ratio: self.predict_compression_ratio(algorithm, density, &pattern_analysis),
396 })
397 }
398
399 fn apply_compression_strategy<T>(
400 &mut self,
401 strategy: &CompressionStrategy,
402 matrix_id: u64,
403 rows: usize,
404 indptr: &[usize],
405 indices: &[usize],
406 data: &[T],
407 ) -> SparseResult<Vec<CompressedBlock>>
408 where
409 T: Float + NumAssign + Send + Sync + Copy + std::fmt::Debug,
410 {
411 let indptr_data = self.serialize_indptr(indptr)?;
413 let indices_data = self.serialize_indices(indices)?;
414 let data_data = self.serialize_data(data)?;
415
416 let mut blocks = Vec::new();
417
418 let indptr_block_id = BlockId::new(matrix_id, 0, 0);
420 let indptr_result = self.compression_engine.compress(
421 &indptr_data,
422 strategy.algorithm,
423 &indptr_block_id,
424 BlockType::IndPtr,
425 )?;
426 blocks.push(CompressedBlock::new(
427 indptr_block_id,
428 BlockType::IndPtr,
429 indptr_result.compressed_data,
430 indptr_data.len(),
431 (indptr_result.compression_ratio.clamp(1.0, 10.0) as u8).max(1),
432 ));
433
434 let indices_block_id = BlockId::new(matrix_id, 0, 1);
436 let indices_result = self.compression_engine.compress(
437 &indices_data,
438 strategy.algorithm,
439 &indices_block_id,
440 BlockType::Indices,
441 )?;
442 blocks.push(CompressedBlock::new(
443 indices_block_id,
444 BlockType::Indices,
445 indices_result.compressed_data,
446 indices_data.len(),
447 (indices_result.compression_ratio.clamp(1.0, 10.0) as u8).max(1),
448 ));
449
450 let data_block_id = BlockId::new(matrix_id, 0, 2);
452 let data_result = self.compression_engine.compress(
453 &data_data,
454 strategy.algorithm,
455 &data_block_id,
456 BlockType::Data,
457 )?;
458 blocks.push(CompressedBlock::new(
459 data_block_id,
460 BlockType::Data,
461 data_result.compressed_data,
462 data_data.len(),
463 (data_result.compression_ratio.clamp(1.0, 10.0) as u8).max(1),
464 ));
465
466 Ok(blocks)
467 }
468
469 fn analyze_sparsity_patterns(
470 &self,
471 indptr: &[usize],
472 indices: &[usize],
473 ) -> SparsityPatternAnalysis {
474 let mut analysis = SparsityPatternAnalysis::default();
475
476 if indptr.len() <= 1 {
477 return analysis;
478 }
479
480 let rows = indptr.len() - 1;
481
482 let mut row_nnz = Vec::new();
484 for row in 0..rows {
485 row_nnz.push(indptr[row + 1] - indptr[row]);
486 }
487
488 analysis.avg_nnz_per_row = row_nnz.iter().sum::<usize>() as f64 / rows as f64;
489 analysis.max_nnz_per_row = *row_nnz.iter().max().unwrap_or(&0);
490 analysis.min_nnz_per_row = *row_nnz.iter().min().unwrap_or(&0);
491
492 analysis.sequential_patterns = self.count_sequential_patterns(indices);
494 analysis.clustering_factor = self.calculate_clustering_factor(indptr, indices);
495 analysis.bandwidth = self.calculate_bandwidth(indptr, indices);
496
497 analysis
498 }
499
500 fn count_sequential_patterns(&self, indices: &[usize]) -> usize {
501 let mut sequential_count = 0;
502 let mut current_sequence = 0;
503
504 for window in indices.windows(2) {
505 if window[1] == window[0] + 1 {
506 current_sequence += 1;
507 } else {
508 if current_sequence >= 3 {
509 sequential_count += current_sequence;
510 }
511 current_sequence = 0;
512 }
513 }
514
515 if current_sequence >= 3 {
516 sequential_count += current_sequence;
517 }
518
519 sequential_count
520 }
521
522 fn calculate_clustering_factor(&self, indptr: &[usize], indices: &[usize]) -> f64 {
523 if indptr.len() <= 1 {
524 return 0.0;
525 }
526
527 let mut total_distance = 0.0;
528 let mut total_pairs = 0;
529
530 let rows = indptr.len() - 1;
531 for row in 0..rows {
532 let start = indptr[row];
533 let end = indptr[row + 1];
534
535 if end > start + 1 {
536 for i in start..(end - 1) {
537 total_distance += (indices[i + 1] - indices[i]) as f64;
538 total_pairs += 1;
539 }
540 }
541 }
542
543 if total_pairs > 0 {
544 total_distance / total_pairs as f64
545 } else {
546 0.0
547 }
548 }
549
550 fn calculate_bandwidth(&self, indptr: &[usize], indices: &[usize]) -> usize {
551 if indptr.len() <= 1 {
552 return 0;
553 }
554
555 let mut max_bandwidth = 0;
556 let rows = indptr.len() - 1;
557
558 for row in 0..rows {
559 let start = indptr[row];
560 let end = indptr[row + 1];
561
562 if end > start {
563 let min_col = indices[start];
564 let max_col = indices[end - 1];
565 let bandwidth = max_col.saturating_sub(min_col);
566 max_bandwidth = max_bandwidth.max(bandwidth);
567 }
568 }
569
570 max_bandwidth
571 }
572
573 fn get_access_pattern_info(&self, _matrix_id: u64) -> AccessPatternInfo {
574 let access_tracker = self.access_tracker.lock().unwrap();
575 let stats = access_tracker.get_statistics();
577 AccessPatternInfo {
578 total_accesses: stats.total_access_events,
579 avg_temporal_locality: 0.5, avg_spatial_locality: 0.5, pattern_count: stats.total_tracked_blocks,
582 }
583 }
584
585 fn select_adaptive_algorithm(
586 &self,
587 density: f64,
588 pattern_analysis: &SparsityPatternAnalysis,
589 access_info: &AccessPatternInfo,
590 ) -> CompressionAlgorithm {
591 if density > 0.1 {
593 CompressionAlgorithm::LZ77
595 } else if pattern_analysis.sequential_patterns
596 > pattern_analysis.avg_nnz_per_row as usize * 10
597 {
598 CompressionAlgorithm::RLE
600 } else if pattern_analysis.clustering_factor < 2.0 {
601 CompressionAlgorithm::Delta
603 } else if access_info.avg_temporal_locality > 0.8 {
604 CompressionAlgorithm::SparseOptimized
606 } else {
607 CompressionAlgorithm::Huffman
609 }
610 }
611
612 fn determine_optimal_block_size(&self, rows: usize, nnz: usize, density: f64) -> usize {
613 let base_block_size = self.config.block_size;
614
615 let size_factor = if rows > 1_000_000 {
617 2.0 } else if rows < 10_000 {
619 0.5 } else {
621 1.0
622 };
623
624 let density_factor = if density > 0.1 {
625 1.5 } else {
627 1.0
628 };
629
630 let nnz_factor = if nnz > 10_000_000 {
631 1.5 } else {
633 1.0
634 };
635
636 let optimal_size =
637 (base_block_size as f64 * size_factor * density_factor * nnz_factor) as usize;
638 optimal_size.clamp(4096, 16 * 1024 * 1024) }
640
641 fn predict_compression_ratio(
642 &self,
643 algorithm: CompressionAlgorithm,
644 density: f64,
645 pattern_analysis: &SparsityPatternAnalysis,
646 ) -> f64 {
647 let base_ratio = algorithm.expected_compression_ratio();
648
649 let adjustment = if pattern_analysis.bandwidth > 100000 {
651 0.8 } else if pattern_analysis.bandwidth < 100 {
653 1.2 } else {
655 1.0
656 };
657
658 base_ratio * adjustment
659 }
660
661 fn handle_out_of_core_storage(&mut self, blocks: &[CompressedBlock]) -> SparseResult<()> {
662 if let Some(ref mut manager) = self.out_of_core_manager {
663 for block in blocks {
664 if block.compressed_data.len() > self.config.block_size {
665 manager.write_block_to_disk(block)?;
667 }
668 }
669 }
670 Ok(())
671 }
672
673 fn get_cached_block(&self, block_id: &BlockId) -> SparseResult<Option<Vec<u8>>> {
674 if let Ok(mut cache) = self.block_cache.lock() {
675 Ok(cache
676 .get(block_id)
677 .map(|cached_block| cached_block.data.clone()))
678 } else {
679 Ok(None)
680 }
681 }
682
683 fn cache_block(&self, block_id: &BlockId, data: &[u8]) -> SparseResult<()> {
684 use super::cache::CachedBlock;
685 if let Ok(mut cache) = self.block_cache.lock() {
686 let cached_block = CachedBlock::new(data.to_vec(), false, 1);
687 cache.insert(block_id.clone(), cached_block);
688 }
689 Ok(())
690 }
691
692 fn record_matrix_access(&self, matrix_id: u64, access_type: AccessType) {
693 if let Ok(mut tracker) = self.access_tracker.lock() {
694 let block_id = BlockId::new(matrix_id, 0, 0); tracker.record_access(block_id, access_type, 1024); }
697 }
698
699 fn decompress_block(
700 &mut self,
701 block: &CompressedBlock,
702 algorithm: CompressionAlgorithm,
703 ) -> SparseResult<Vec<u8>> {
704 self.compression_engine
705 .decompress(&block.compressed_data, algorithm, block.original_size)
706 }
707
708 fn create_uncompressed_matrix<T>(
709 &self,
710 matrix_id: u64,
711 rows: usize,
712 indptr: &[usize],
713 indices: &[usize],
714 data: &[T],
715 ) -> SparseResult<CompressedMatrix<T>>
716 where
717 T: Float + NumAssign + Send + Sync + Copy + std::fmt::Debug,
718 {
719 let mut matrix = CompressedMatrix::new(
720 matrix_id,
721 rows,
722 if !indptr.is_empty() {
723 *indices.iter().max().unwrap_or(&0) + 1
724 } else {
725 0
726 },
727 CompressionAlgorithm::None,
728 self.config.block_size,
729 );
730
731 let indptr_data = self.serialize_indptr(indptr)?;
733 let indices_data = self.serialize_indices(indices)?;
734 let data_data = self.serialize_data(data)?;
735
736 matrix.add_block(CompressedBlock::new(
737 BlockId::new(matrix_id, 0, 0),
738 BlockType::IndPtr,
739 indptr_data.clone(),
740 indptr_data.len(),
741 0,
742 ));
743
744 matrix.add_block(CompressedBlock::new(
745 BlockId::new(matrix_id, 0, 1),
746 BlockType::Indices,
747 indices_data.clone(),
748 indices_data.len(),
749 0,
750 ));
751
752 matrix.add_block(CompressedBlock::new(
753 BlockId::new(matrix_id, 0, 2),
754 BlockType::Data,
755 data_data.clone(),
756 data_data.len(),
757 0,
758 ));
759
760 Ok(matrix)
761 }
762
763 fn update_compression_stats(
764 &self,
765 original_size: usize,
766 blocks: &[CompressedBlock],
767 compression_time: f64,
768 ) {
769 if let Ok(mut stats) = self.compression_stats.lock() {
770 let compressed_size = blocks
771 .iter()
772 .map(|b| b.compressed_data.len())
773 .sum::<usize>();
774
775 stats.update_compression(original_size, compressed_size, compression_time);
776 }
777 }
778
779 fn get_cache_stats(&self) -> CacheStats {
780 if let Ok(cache) = self.block_cache.lock() {
781 let stats = cache.get_stats();
782 CacheStats {
783 hits: stats.hit_count,
784 misses: stats.miss_count,
785 hit_ratio: if stats.hit_count + stats.miss_count > 0 {
786 stats.hit_count as f64 / (stats.hit_count + stats.miss_count) as f64
787 } else {
788 0.0
789 },
790 }
791 } else {
792 CacheStats {
793 hits: 0,
794 misses: 0,
795 hit_ratio: 0.0,
796 }
797 }
798 }
799
800 fn serialize_indptr(&self, indptr: &[usize]) -> SparseResult<Vec<u8>> {
802 let mut serialized = Vec::new();
803 serialized.extend_from_slice(&indptr.len().to_le_bytes());
804 for &value in indptr {
805 serialized.extend_from_slice(&value.to_le_bytes());
806 }
807 Ok(serialized)
808 }
809
810 fn serialize_indices(&self, indices: &[usize]) -> SparseResult<Vec<u8>> {
811 let mut serialized = Vec::new();
812 serialized.extend_from_slice(&indices.len().to_le_bytes());
813 for &value in indices {
814 serialized.extend_from_slice(&value.to_le_bytes());
815 }
816 Ok(serialized)
817 }
818
819 fn serialize_data<T>(&self, data: &[T]) -> SparseResult<Vec<u8>>
820 where
821 T: Float + NumAssign + Send + Sync + Copy,
822 {
823 let mut serialized = Vec::new();
824 serialized.extend_from_slice(&data.len().to_le_bytes());
825 for &value in data {
826 let bytes = value.to_f64().unwrap_or(0.0).to_le_bytes();
827 serialized.extend_from_slice(&bytes);
828 }
829 Ok(serialized)
830 }
831
832 fn parse_indptr_data(&self, data: &[u8]) -> SparseResult<Vec<usize>> {
834 if data.len() < 8 {
835 return Ok(Vec::new());
836 }
837
838 let length = usize::from_le_bytes([
839 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
840 ]);
841 let mut indptr = Vec::with_capacity(length);
842
843 let mut offset = 8;
844 for _ in 0..length {
845 if offset + 8 <= data.len() {
846 let value = usize::from_le_bytes([
847 data[offset],
848 data[offset + 1],
849 data[offset + 2],
850 data[offset + 3],
851 data[offset + 4],
852 data[offset + 5],
853 data[offset + 6],
854 data[offset + 7],
855 ]);
856 indptr.push(value);
857 offset += 8;
858 }
859 }
860
861 Ok(indptr)
862 }
863
864 fn parse_indices_data(&self, data: &[u8]) -> SparseResult<Vec<usize>> {
865 self.parse_indptr_data(data) }
867
868 fn parse_data_values<T>(&self, data: &[u8]) -> SparseResult<Vec<T>>
869 where
870 T: Float + NumAssign + Send + Sync + Copy + scirs2_core::numeric::FromPrimitive,
871 {
872 if data.len() < 8 {
873 return Ok(Vec::new());
874 }
875
876 let length = usize::from_le_bytes([
877 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
878 ]);
879 let mut values = Vec::with_capacity(length);
880
881 let mut offset = 8;
882 for _ in 0..length {
883 if offset + 8 <= data.len() {
884 let value_f64 = f64::from_le_bytes([
885 data[offset],
886 data[offset + 1],
887 data[offset + 2],
888 data[offset + 3],
889 data[offset + 4],
890 data[offset + 5],
891 data[offset + 6],
892 data[offset + 7],
893 ]);
894 if let Some(value) = T::from_f64(value_f64) {
895 values.push(value);
896 }
897 offset += 8;
898 }
899 }
900
901 Ok(values)
902 }
903
904 fn parse_combined_data<T>(&self, _data: &[u8]) -> SparseResult<(Vec<usize>, Vec<usize>, Vec<T>)>
905 where
906 T: Float + NumAssign + Send + Sync + Copy,
907 {
908 Ok((Vec::new(), Vec::new(), Vec::new()))
910 }
911}
912
913#[cfg(test)]
914mod tests {
915 use super::*;
916 use tempfile::TempDir;
917
918 #[test]
919 fn test_compressor_creation() {
920 let config = AdaptiveCompressionConfig::default();
921 let compressor = AdaptiveMemoryCompressor::new(config);
922 assert!(compressor.is_ok());
923 }
924
925 #[test]
926 fn test_matrix_compression_roundtrip() {
927 let config = AdaptiveCompressionConfig {
928 compression_algorithm: CompressionAlgorithm::None,
929 out_of_core: false,
930 memory_mapping: false,
931 ..Default::default()
932 };
933 let mut compressor = AdaptiveMemoryCompressor::new(config).unwrap();
934
935 let indptr = vec![0, 2, 3];
936 let indices = vec![0, 1, 1];
937 let data = vec![1.0, 2.0, 3.0];
938
939 let compressed = compressor
940 .compress_matrix(1, 2, &indptr, &indices, &data)
941 .unwrap();
942
943 let (decompressed_indptr, decompressed_indices, decompressed_data) =
944 compressor.decompress_matrix(&compressed).unwrap();
945
946 assert_eq!(decompressed_indptr, indptr);
947 assert_eq!(decompressed_indices, indices);
948 assert_eq!(decompressed_data.len(), data.len());
949 }
950
951 #[test]
952 fn test_memory_stats() {
953 let config = AdaptiveCompressionConfig::default();
954 let compressor = AdaptiveMemoryCompressor::new(config).unwrap();
955 let stats = compressor.get_memory_stats();
956
957 assert_eq!(stats.current_memory_usage, 0);
958 assert!(stats.memory_usage_ratio >= 0.0);
959 }
960
961 #[test]
962 fn test_access_pattern_optimization() {
963 let config = AdaptiveCompressionConfig::default();
964 let mut compressor = AdaptiveMemoryCompressor::new(config).unwrap();
965
966 compressor.optimize_for_sequential_access();
968
969 compressor.optimize_for_random_access();
971
972 }
974
975 #[test]
976 fn test_compressor_reset() {
977 let temp_dir = TempDir::new().unwrap();
978 let config = AdaptiveCompressionConfig {
979 temp_directory: temp_dir.path().to_str().unwrap().to_string(),
980 ..Default::default()
981 };
982 let mut compressor = AdaptiveMemoryCompressor::new(config).unwrap();
983
984 let indptr = vec![0, 1];
986 let indices = vec![0];
987 let data = vec![1.0];
988
989 let _ = compressor.compress_matrix(1, 1, &indptr, &indices, &data);
990
991 let result = compressor.reset();
993 assert!(result.is_ok());
994
995 let stats = compressor.get_memory_stats();
996 assert_eq!(stats.current_memory_usage, 0);
997 }
998}