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