use crate::error::{StatsError, StatsResult};
use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
use scirs2_core::numeric::{Float, NumCast, One, Zero};
use serde::{Deserialize, Serialize};
use std::alloc::{GlobalAlloc, Layout, System};
use std::collections::{HashMap, VecDeque};
use std::mem;
use std::sync::{Arc, Mutex, RwLock};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct MemoryOptimizationConfig {
pub memory_limit: usize,
pub enable_streaming: bool,
pub enable_memory_mapping: bool,
pub enable_cache_optimization: bool,
pub enable_adaptive_allocation: bool,
pub cache_linesize: usize,
pub memory_poolsize: usize,
pub enable_compression: bool,
pub streaming_chunksize: usize,
}
impl Default for MemoryOptimizationConfig {
fn default() -> Self {
Self {
memory_limit: 1024 * 1024 * 1024, enable_streaming: true,
enable_memory_mapping: true,
enable_cache_optimization: true,
enable_adaptive_allocation: true,
cache_linesize: 64, memory_poolsize: 64 * 1024 * 1024, enable_compression: false, streaming_chunksize: 10000, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct MemoryProfile {
pub current_usage: usize,
pub peak_usage: usize,
pub allocation_count: usize,
pub deallocation_count: usize,
pub fragmentation_ratio: f64,
pub cache_hit_ratio: f64,
pub efficiency_score: f64,
pub active_pools: Vec<MemoryPoolStats>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct MemoryPoolStats {
pub pool_id: String,
pub poolsize: usize,
pub used_bytes: usize,
pub allocations: usize,
pub avg_allocationsize: f64,
}
pub struct StreamingStatsCalculator<F> {
config: MemoryOptimizationConfig,
count: usize,
sum: F,
sum_squares: F,
min_value: Option<F>,
max_value: Option<F>,
memory_profile: Arc<Mutex<MemoryProfile>>,
computation_buffer: VecDeque<F>,
}
pub struct CacheOptimizedMatrix<F> {
data: Array2<F>,
blocksize: usize,
memory_layout: MatrixLayout,
cache_linesize: usize,
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub enum MatrixLayout {
RowMajor,
ColumnMajor,
BlockedRowMajor,
BlockedColumnMajor,
ZOrderCurve,
}
pub struct AdaptiveStatsAllocator {
config: MemoryOptimizationConfig,
memory_pools: HashMap<String, Arc<Mutex<MemoryPool>>>,
allocation_patterns: Arc<RwLock<AllocationPatternAnalyzer>>,
global_stats: Arc<Mutex<MemoryProfile>>,
}
#[allow(dead_code)]
struct MemoryPool {
pool_id: String,
base_ptr: *mut u8,
poolsize: usize,
usedsize: usize,
free_blocks: Vec<MemoryBlock>,
used_blocks: Vec<MemoryBlock>,
allocation_count: usize,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct MemoryBlock {
offset: usize,
size: usize,
alignment: usize,
allocation_time: std::time::Instant,
}
#[allow(dead_code)]
struct AllocationPatternAnalyzer {
allocation_history: VecDeque<AllocationEvent>,
pattern_cache: HashMap<String, AllocationPattern>,
prediction_accuracy: f64,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct AllocationEvent {
size: usize,
alignment: usize,
lifetime: std::time::Duration,
operation_type: String,
timestamp: std::time::Instant,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct AllocationPattern {
typicalsize: usize,
typical_alignment: usize,
expected_lifetime: std::time::Duration,
frequency: f64,
confidence: f64,
}
pub struct MemoryMappedStatsProcessor {
config: MemoryOptimizationConfig,
mapped_files: HashMap<String, MemoryMappedFile>,
cache_manager: CacheManager,
}
#[allow(dead_code)]
struct MemoryMappedFile {
file_path: String,
filesize: usize,
mapped_ptr: *const u8,
access_pattern: AccessPattern,
}
#[derive(Debug, Clone)]
struct AccessPattern {
sequential_ratio: f64,
random_ratio: f64,
hot_regions: Vec<MemoryRegion>,
}
#[derive(Debug, Clone)]
struct MemoryRegion {
start_offset: usize,
end_offset: usize,
access_frequency: f64,
last_access: std::time::Instant,
}
#[allow(dead_code)]
struct CacheManager {
cachesize: usize,
cache_entries: HashMap<u64, CacheEntry>,
access_order: VecDeque<u64>,
hit_count: usize,
miss_count: usize,
}
#[allow(dead_code)]
struct CacheEntry {
data: Vec<u8>,
access_count: usize,
last_access: std::time::Instant,
size: usize,
}
impl<F> StreamingStatsCalculator<F>
where
F: Float + NumCast + Zero + One + Send + Sync + std::fmt::Display,
{
pub fn new(config: MemoryOptimizationConfig) -> Self {
Self {
config,
count: 0,
sum: F::zero(),
sum_squares: F::zero(),
min_value: None,
max_value: None,
memory_profile: Arc::new(Mutex::new(MemoryProfile::new())),
computation_buffer: VecDeque::with_capacity(1000), }
}
pub fn process_chunk(&mut self, chunk: ArrayView1<F>) -> StatsResult<()> {
self.update_memory_profile();
for &value in chunk.iter() {
self.count += 1;
self.sum = self.sum + value;
self.sum_squares = self.sum_squares + value * value;
match self.min_value {
None => self.min_value = Some(value),
Some(current_min) => {
if value < current_min {
self.min_value = Some(value);
}
}
}
match self.max_value {
None => self.max_value = Some(value),
Some(current_max) => {
if value > current_max {
self.max_value = Some(value);
}
}
}
if self.computation_buffer.len() >= self.config.streaming_chunksize {
self.computation_buffer.pop_front();
}
self.computation_buffer.push_back(value);
}
Ok(())
}
pub fn get_statistics(&self) -> StatsResult<StreamingStatistics<F>> {
if self.count == 0 {
return Err(StatsError::InvalidArgument(
"No data processed yet".to_string(),
));
}
let count_f = F::from(self.count).expect("Failed to convert to float");
let mean = self.sum / count_f;
let variance = if self.count > 1 {
let count_minus_one = F::from(self.count - 1).expect("Failed to convert to float");
(self.sum_squares - self.sum * self.sum / count_f) / count_minus_one
} else {
F::zero()
};
let std_dev = variance.sqrt();
Ok(StreamingStatistics {
count: self.count,
mean,
variance,
std_dev,
min: self.min_value.unwrap_or(F::zero()),
max: self.max_value.unwrap_or(F::zero()),
memory_efficiency: self.calculate_memory_efficiency(),
})
}
fn calculate_memory_efficiency(&self) -> f64 {
let theoretical_minimum = mem::size_of::<F>() * self.count;
let actual_usage = self.estimate_memory_usage();
if actual_usage > 0 {
(theoretical_minimum as f64 / actual_usage as f64 * 100.0).min(100.0)
} else {
100.0
}
}
fn estimate_memory_usage(&self) -> usize {
mem::size_of::<Self>() + self.computation_buffer.len() * mem::size_of::<F>()
}
fn update_memory_profile(&mut self) {
if let Ok(mut profile) = self.memory_profile.lock() {
let current_usage = self.estimate_memory_usage();
profile.current_usage = current_usage;
if current_usage > profile.peak_usage {
profile.peak_usage = current_usage;
}
profile.allocation_count += 1;
profile.efficiency_score = self.calculate_memory_efficiency();
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct StreamingStatistics<F> {
pub count: usize,
pub mean: F,
pub variance: F,
pub std_dev: F,
pub min: F,
pub max: F,
pub memory_efficiency: f64,
}
impl<F> CacheOptimizedMatrix<F>
where
F: Float + NumCast + Zero + One + Clone + 'static + std::fmt::Display,
{
pub fn new(data: Array2<F>, layout: MatrixLayout, cache_linesize: usize) -> Self {
let optimal_blocksize =
Self::calculate_optimal_blocksize(data.nrows(), data.ncols(), cache_linesize);
let mut matrix = Self {
data,
blocksize: optimal_blocksize,
memory_layout: layout,
cache_linesize,
};
matrix.optimize_layout();
matrix
}
pub fn multiply_optimized(
&self,
other: &CacheOptimizedMatrix<F>,
) -> StatsResult<CacheOptimizedMatrix<F>> {
if self.data.ncols() != other.data.nrows() {
return Err(StatsError::DimensionMismatch(
"Matrix dimensions incompatible for multiplication".to_string(),
));
}
let resultdata = match self.memory_layout {
MatrixLayout::BlockedRowMajor | MatrixLayout::BlockedColumnMajor => {
self.blocked_multiply(&other.data)?
}
_ => self.standard_multiply(&other.data)?,
};
Ok(CacheOptimizedMatrix::new(
resultdata,
self.memory_layout,
self.cache_linesize,
))
}
pub fn correlation_matrix(&self) -> StatsResult<CacheOptimizedMatrix<F>> {
let (_n_samples_, n_features) = self.data.dim();
let means = self.compute_column_means_optimized()?;
let mut correlation = Array2::zeros((n_features, n_features));
let blocksize = self.blocksize;
for i_block in (0..n_features).step_by(blocksize) {
for j_block in (0..n_features).step_by(blocksize) {
let i_end = (i_block + blocksize).min(n_features);
let j_end = (j_block + blocksize).min(n_features);
for i in i_block..i_end {
for j in j_block..j_end {
let correlation_value = self.compute_correlation_pair(i, j, &means)?;
correlation[[i, j]] = correlation_value;
}
}
}
}
Ok(CacheOptimizedMatrix::new(
correlation,
MatrixLayout::BlockedRowMajor,
self.cache_linesize,
))
}
fn calculate_optimal_blocksize(_rows: usize, cols: usize, cache_linesize: usize) -> usize {
let elementsize = mem::size_of::<F>();
let elements_per_cache_line = cache_linesize / elementsize;
let target_block_elements = (32 * 1024) / elementsize; let max_dimension = _rows.max(cols);
((target_block_elements as f64).sqrt() as usize)
.min(max_dimension)
.max(elements_per_cache_line)
}
fn optimize_layout(&mut self) {
match self.memory_layout {
MatrixLayout::ZOrderCurve => {
self.data = self.convert_to_z_order();
}
MatrixLayout::BlockedRowMajor => {
self.data = self.convert_to_blocked_layout(true);
}
MatrixLayout::BlockedColumnMajor => {
self.data = self.convert_to_blocked_layout(false);
}
_ => {
}
}
}
fn convert_to_z_order(&self) -> Array2<F> {
self.data.clone() }
fn convert_to_blocked_layout(&self, rowmajor: bool) -> Array2<F> {
self.data.clone() }
fn blocked_multiply(&self, other: &Array2<F>) -> StatsResult<Array2<F>> {
let (m, k) = self.data.dim();
let (k2, n) = other.dim();
if k != k2 {
return Err(StatsError::DimensionMismatch(
"Matrix dimensions incompatible".to_string(),
));
}
let mut result = Array2::zeros((m, n));
let blocksize = self.blocksize;
for i_block in (0..m).step_by(blocksize) {
for j_block in (0..n).step_by(blocksize) {
for k_block in (0..k).step_by(blocksize) {
let i_end = (i_block + blocksize).min(m);
let j_end = (j_block + blocksize).min(n);
let k_end = (k_block + blocksize).min(k);
for i in i_block..i_end {
for j in j_block..j_end {
let mut sum = F::zero();
for k_idx in k_block..k_end {
sum = sum + self.data[[i, k_idx]] * other[[k_idx, j]];
}
result[[i, j]] = result[[i, j]] + sum;
}
}
}
}
}
Ok(result)
}
fn standard_multiply(&self, other: &Array2<F>) -> StatsResult<Array2<F>> {
let result = self.data.dot(other);
Ok(result)
}
fn compute_column_means_optimized(&self) -> StatsResult<Array1<F>> {
let n_features = self.data.ncols();
let n_samples_f = F::from(self.data.nrows()).expect("Operation failed");
let mut means = Array1::zeros(n_features);
let blocksize = self.blocksize;
for col_block in (0..n_features).step_by(blocksize) {
let col_end = (col_block + blocksize).min(n_features);
for col in col_block..col_end {
let column_sum = self.data.column(col).sum();
means[col] = column_sum / n_samples_f;
}
}
Ok(means)
}
fn compute_correlation_pair(
&self,
col_i: usize,
col_j: usize,
means: &Array1<F>,
) -> StatsResult<F> {
if col_i == col_j {
return Ok(F::one());
}
let n_samples_ = self.data.nrows();
let _n_samples_f = F::from(n_samples_).expect("Failed to convert to float");
let mean_i = means[col_i];
let mean_j = means[col_j];
let mut numerator = F::zero();
let mut sum_sq_i = F::zero();
let mut sum_sq_j = F::zero();
for row in 0..n_samples_ {
let val_i = self.data[[row, col_i]] - mean_i;
let val_j = self.data[[row, col_j]] - mean_j;
numerator = numerator + val_i * val_j;
sum_sq_i = sum_sq_i + val_i * val_i;
sum_sq_j = sum_sq_j + val_j * val_j;
}
let denominator = (sum_sq_i * sum_sq_j).sqrt();
if denominator > F::epsilon() {
Ok(numerator / denominator)
} else {
Ok(F::zero())
}
}
}
impl AdaptiveStatsAllocator {
pub fn new(config: MemoryOptimizationConfig) -> Self {
let mut allocator = Self {
config: config.clone(),
memory_pools: HashMap::new(),
allocation_patterns: Arc::new(RwLock::new(AllocationPatternAnalyzer::new())),
global_stats: Arc::new(Mutex::new(MemoryProfile::new())),
};
let _ = allocator.create_memory_pool("float_arrays", config.memory_poolsize / 4);
let _ = allocator.create_memory_pool("matrix_operations", config.memory_poolsize / 2);
let _ = allocator.create_memory_pool("temporary_buffers", config.memory_poolsize / 4);
allocator
}
pub fn create_memory_pool(&mut self, poolid: &str, size: usize) -> StatsResult<()> {
let pool = Arc::new(Mutex::new(MemoryPool::new(poolid, size)?));
self.memory_pools.insert(poolid.to_string(), pool);
Ok(())
}
pub fn allocate_optimized(
&self,
size: usize,
alignment: usize,
operation_type: &str,
) -> StatsResult<*mut u8> {
let predicted_pool = self.predict_optimal_pool(size, alignment, operation_type);
if let Some(pool) = self.memory_pools.get(&predicted_pool) {
if let Ok(mut pool_guard) = pool.lock() {
if let Ok(ptr) = pool_guard.allocate(size, alignment) {
self.record_allocation_event(size, alignment, operation_type);
return Ok(ptr);
}
}
}
let layout = Layout::from_size_align(size, alignment)
.map_err(|e| StatsError::ComputationError(format!("Invalid layout: {}", e)))?;
let ptr = unsafe { System.alloc(layout) };
if ptr.is_null() {
return Err(StatsError::ComputationError(
"Memory allocation failed".to_string(),
));
}
self.record_allocation_event(size, alignment, operation_type);
Ok(ptr)
}
fn predict_optimal_pool(&self, size: usize, alignment: usize, operationtype: &str) -> String {
if let Ok(analyzer) = self.allocation_patterns.read() {
if let Some(pattern) = analyzer.get_pattern(operationtype) {
if pattern.typicalsize <= 1024 {
return "temporary_buffers".to_string();
} else if pattern.typicalsize <= 64 * 1024 {
return "float_arrays".to_string();
} else {
return "matrix_operations".to_string();
}
}
}
if size <= 1024 {
"temporary_buffers".to_string()
} else if size <= 64 * 1024 {
"float_arrays".to_string()
} else {
"matrix_operations".to_string()
}
}
fn record_allocation_event(&self, size: usize, alignment: usize, operationtype: &str) {
if let Ok(mut analyzer) = self.allocation_patterns.write() {
analyzer.record_allocation(size, alignment, operationtype);
}
if let Ok(mut stats) = self.global_stats.lock() {
stats.allocation_count += 1;
stats.current_usage += size;
if stats.current_usage > stats.peak_usage {
stats.peak_usage = stats.current_usage;
}
}
}
pub fn get_memory_profile(&self) -> MemoryProfile {
if let Ok(profile) = self.global_stats.lock() {
profile.clone()
} else {
MemoryProfile::new()
}
}
pub fn optimize_pools(&mut self) -> StatsResult<()> {
let pools_to_create: Vec<(String, usize)> = {
if let Ok(analyzer) = self.allocation_patterns.read() {
analyzer
.pattern_cache
.iter()
.filter(|(_, pattern)| pattern.confidence > 0.8)
.map(|(operation_type, pattern)| {
let pool_name = format!("specialized_{}", operation_type);
let optimalsize = (pattern.typicalsize * 100).max(64 * 1024);
(pool_name, optimalsize)
})
.collect()
} else {
Vec::new()
}
};
for (pool_name, optimalsize) in pools_to_create {
self.create_memory_pool(&pool_name, optimalsize)?;
}
Ok(())
}
}
impl MemoryPool {
fn new(poolid: &str, size: usize) -> StatsResult<Self> {
let layout = Layout::from_size_align(size, 64) .map_err(|e| StatsError::ComputationError(format!("Invalid layout: {}", e)))?;
let base_ptr = unsafe { System.alloc(layout) };
if base_ptr.is_null() {
return Err(StatsError::ComputationError(
"Memory pool allocation failed".to_string(),
));
}
Ok(Self {
pool_id: poolid.to_string(),
base_ptr,
poolsize: size,
usedsize: 0,
free_blocks: vec![MemoryBlock {
offset: 0,
size,
alignment: 64,
allocation_time: std::time::Instant::now(),
}],
used_blocks: Vec::new(),
allocation_count: 0,
})
}
fn allocate(&mut self, size: usize, alignment: usize) -> StatsResult<*mut u8> {
for (index, block) in self.free_blocks.iter().enumerate() {
let aligned_offset = self.align_offset(block.offset, alignment);
let totalsize = aligned_offset - block.offset + size;
if totalsize <= block.size {
let used_block = MemoryBlock {
offset: aligned_offset,
size,
alignment,
allocation_time: std::time::Instant::now(),
};
let remainingsize = block.size - totalsize;
if remainingsize > 0 {
let remaining_block = MemoryBlock {
offset: aligned_offset + size,
size: remainingsize,
alignment: 1,
allocation_time: std::time::Instant::now(),
};
self.free_blocks[index] = remaining_block;
} else {
self.free_blocks.remove(index);
}
self.used_blocks.push(used_block);
self.usedsize += size;
self.allocation_count += 1;
let ptr = unsafe { self.base_ptr.add(aligned_offset) };
return Ok(ptr);
}
}
Err(StatsError::ComputationError(
"No suitable block available in pool".to_string(),
))
}
fn align_offset(&self, offset: usize, alignment: usize) -> usize {
(offset + alignment - 1) & !(alignment - 1)
}
}
impl AllocationPatternAnalyzer {
fn new() -> Self {
Self {
allocation_history: VecDeque::with_capacity(10000),
pattern_cache: HashMap::new(),
prediction_accuracy: 0.0,
}
}
fn record_allocation(&mut self, size: usize, alignment: usize, operationtype: &str) {
let event = AllocationEvent {
size,
alignment,
lifetime: std::time::Duration::from_secs(0), operation_type: operationtype.to_string(),
timestamp: std::time::Instant::now(),
};
self.allocation_history.push_back(event);
if self.allocation_history.len() > 10000 {
self.allocation_history.pop_front();
}
if self.allocation_history.len().is_multiple_of(100) {
self.update_patterns();
}
}
fn update_patterns(&mut self) {
let mut operation_groups: HashMap<String, Vec<&AllocationEvent>> = HashMap::new();
for event in &self.allocation_history {
operation_groups
.entry(event.operation_type.clone())
.or_default()
.push(event);
}
for (operation_type, events) in operation_groups {
if events.len() >= 10 {
let pattern = self.analyze_allocation_pattern(&events);
self.pattern_cache.insert(operation_type, pattern);
}
}
}
fn analyze_allocation_pattern(&self, events: &[&AllocationEvent]) -> AllocationPattern {
let sizes: Vec<usize> = events.iter().map(|e| e.size).collect();
let alignments: Vec<usize> = events.iter().map(|e| e.alignment).collect();
let typicalsize = self.calculate_median(&sizes);
let typical_alignment = self.calculate_mode(&alignments);
let frequency = events.len() as f64 / self.allocation_history.len() as f64;
let size_variance = self.calculate_variance(&sizes);
let confidence = 1.0 / (1.0 + size_variance / typicalsize as f64);
AllocationPattern {
typicalsize,
typical_alignment,
expected_lifetime: std::time::Duration::from_millis(100), frequency,
confidence,
}
}
fn calculate_median(&self, values: &[usize]) -> usize {
let mut sorted = values.to_vec();
sorted.sort_unstable();
sorted[sorted.len() / 2]
}
fn calculate_mode(&self, values: &[usize]) -> usize {
let mut counts = HashMap::new();
for &value in values {
*counts.entry(value).or_insert(0) += 1;
}
counts
.into_iter()
.max_by_key(|(_, count)| *count)
.map(|(_, value)| value)
.unwrap_or(64) }
fn calculate_variance(&self, values: &[usize]) -> f64 {
let mean = values.iter().sum::<usize>() as f64 / values.len() as f64;
let variance = values
.iter()
.map(|&x| (x as f64 - mean).powi(2))
.sum::<f64>()
/ values.len() as f64;
variance
}
fn get_pattern(&self, operationtype: &str) -> Option<&AllocationPattern> {
self.pattern_cache.get(operationtype)
}
}
impl MemoryProfile {
fn new() -> Self {
Self {
current_usage: 0,
peak_usage: 0,
allocation_count: 0,
deallocation_count: 0,
fragmentation_ratio: 0.0,
cache_hit_ratio: 0.0,
efficiency_score: 100.0,
active_pools: Vec::new(),
}
}
}
pub struct MemoryOptimizationSuite {
config: MemoryOptimizationConfig,
allocator: AdaptiveStatsAllocator,
cache_manager: CacheManager,
}
impl MemoryOptimizationSuite {
pub fn new(config: MemoryOptimizationConfig) -> Self {
let allocator = AdaptiveStatsAllocator::new(config.clone());
let cache_manager = CacheManager::new(config.memory_poolsize / 8);
Self {
config,
allocator,
cache_manager,
}
}
pub fn optimized_correlation_matrix<F>(&mut self, data: ArrayView2<F>) -> StatsResult<Array2<F>>
where
F: Float + NumCast + Zero + One + Clone + Send + Sync + 'static + std::fmt::Display,
{
let (n_samples_, n_features) = data.dim();
let datasize = n_samples_ * n_features * mem::size_of::<F>();
if datasize > self.config.memory_limit {
self.streaming_correlation_matrix(data)
} else {
let cache_optimized = CacheOptimizedMatrix::new(
data.to_owned(),
MatrixLayout::BlockedRowMajor,
self.config.cache_linesize,
);
let result = cache_optimized.correlation_matrix()?;
Ok(result.data)
}
}
fn streaming_correlation_matrix<F>(&mut self, data: ArrayView2<F>) -> StatsResult<Array2<F>>
where
F: Float + NumCast + Zero + One + Clone + 'static + std::fmt::Display,
{
let (n_samples_, n_features) = data.dim();
let chunksize = self.config.streaming_chunksize;
let mut means = vec![F::zero(); n_features];
let _variances = vec![F::zero(); n_features];
for chunk_start in (0..n_samples_).step_by(chunksize) {
let chunk_end = (chunk_start + chunksize).min(n_samples_);
let chunk = data.slice(scirs2_core::ndarray::s![chunk_start..chunk_end, ..]);
for (feature_idx, column) in chunk.axis_iter(Axis(1)).enumerate() {
for &value in column.iter() {
means[feature_idx] = means[feature_idx] + value;
}
}
}
let n_samples_f = F::from(n_samples_).expect("Failed to convert to float");
for mean in &mut means {
*mean = *mean / n_samples_f;
}
let mut correlation_matrix = Array2::zeros((n_features, n_features));
for i in 0..n_features {
for j in i..n_features {
correlation_matrix[[i, j]] =
self.compute_streaming_correlation(&data, i, j, means[i], means[j])?;
correlation_matrix[[j, i]] = correlation_matrix[[i, j]];
}
}
Ok(correlation_matrix)
}
fn compute_streaming_correlation<F>(
&self,
data: &ArrayView2<F>,
feature_i: usize,
feature_j: usize,
mean_i: F,
mean_j: F,
) -> StatsResult<F>
where
F: Float + NumCast + Zero + One + std::fmt::Display,
{
if feature_i == feature_j {
return Ok(F::one());
}
let mut numerator = F::zero();
let mut sum_sq_i = F::zero();
let mut sum_sq_j = F::zero();
let chunksize = self.config.streaming_chunksize;
let n_samples_ = data.nrows();
for chunk_start in (0..n_samples_).step_by(chunksize) {
let chunk_end = (chunk_start + chunksize).min(n_samples_);
for row in chunk_start..chunk_end {
let val_i = data[[row, feature_i]] - mean_i;
let val_j = data[[row, feature_j]] - mean_j;
numerator = numerator + val_i * val_j;
sum_sq_i = sum_sq_i + val_i * val_i;
sum_sq_j = sum_sq_j + val_j * val_j;
}
}
let denominator = (sum_sq_i * sum_sq_j).sqrt();
if denominator > F::epsilon() {
Ok(numerator / denominator)
} else {
Ok(F::zero())
}
}
pub fn get_optimization_report(&self) -> MemoryOptimizationReport {
let memory_profile = self.allocator.get_memory_profile();
let cache_stats = self.cache_manager.get_statistics();
MemoryOptimizationReport {
config: self.config.clone(),
memory_profile,
cache_statistics: cache_stats,
optimization_recommendations: self.generate_optimization_recommendations(),
}
}
fn generate_optimization_recommendations(&self) -> Vec<MemoryOptimizationRecommendation> {
let mut recommendations = Vec::new();
let profile = self.allocator.get_memory_profile();
if profile.efficiency_score < 70.0 {
recommendations.push(MemoryOptimizationRecommendation {
priority: 5,
category: "Memory Efficiency".to_string(),
description: "Low memory efficiency detected".to_string(),
suggestion: "Consider using streaming algorithms for large datasets".to_string(),
expected_impact: "20-50% memory reduction".to_string(),
});
}
if profile.cache_hit_ratio < 0.8 {
recommendations.push(MemoryOptimizationRecommendation {
priority: 4,
category: "Cache Performance".to_string(),
description: "Low cache hit ratio detected".to_string(),
suggestion: "Use cache-optimized matrix layouts".to_string(),
expected_impact: "10-30% performance improvement".to_string(),
});
}
if profile.fragmentation_ratio > 0.3 {
recommendations.push(MemoryOptimizationRecommendation {
priority: 3,
category: "Memory Fragmentation".to_string(),
description: "High memory fragmentation detected".to_string(),
suggestion: "Enable memory pool optimization".to_string(),
expected_impact: "Reduced allocation overhead".to_string(),
});
}
recommendations
}
}
impl CacheManager {
fn new(cachesize: usize) -> Self {
Self {
cachesize,
cache_entries: HashMap::new(),
access_order: VecDeque::new(),
hit_count: 0,
miss_count: 0,
}
}
fn get_statistics(&self) -> CacheStatistics {
let total_accesses = self.hit_count + self.miss_count;
let hit_ratio = if total_accesses > 0 {
self.hit_count as f64 / total_accesses as f64
} else {
0.0
};
CacheStatistics {
hit_ratio,
total_entries: self.cache_entries.len(),
totalsize: self.cache_entries.values().map(|e| e.size).sum(),
total_accesses,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct MemoryOptimizationReport {
pub config: MemoryOptimizationConfig,
pub memory_profile: MemoryProfile,
pub cache_statistics: CacheStatistics,
pub optimization_recommendations: Vec<MemoryOptimizationRecommendation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct CacheStatistics {
pub hit_ratio: f64,
pub total_entries: usize,
pub totalsize: usize,
pub total_accesses: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct MemoryOptimizationRecommendation {
pub priority: u8,
pub category: String,
pub description: String,
pub suggestion: String,
pub expected_impact: String,
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::array;
#[test]
fn test_streaming_stats_calculator() {
let config = MemoryOptimizationConfig::default();
let mut calculator = StreamingStatsCalculator::new(config);
let chunk1 = array![1.0, 2.0, 3.0];
let chunk2 = array![4.0, 5.0, 6.0];
calculator
.process_chunk(chunk1.view())
.expect("Operation failed");
calculator
.process_chunk(chunk2.view())
.expect("Operation failed");
let stats = calculator.get_statistics().expect("Operation failed");
assert_eq!(stats.count, 6);
assert!((stats.mean - 3.5).abs() < 1e-10);
}
#[test]
fn test_cache_optimized_matrix() {
let data = array![[1.0, 2.0], [3.0, 4.0]];
let matrix = CacheOptimizedMatrix::new(data, MatrixLayout::BlockedRowMajor, 64);
assert_eq!(matrix.data.nrows(), 2);
assert_eq!(matrix.data.ncols(), 2);
}
#[test]
fn test_adaptive_allocator() {
let config = MemoryOptimizationConfig::default();
let allocator = AdaptiveStatsAllocator::new(config);
let ptr = allocator
.allocate_optimized(1024, 8, "test_operation")
.expect("Operation failed");
assert!(!ptr.is_null());
}
#[test]
fn test_memory_optimization_suite() {
let config = MemoryOptimizationConfig::default();
let mut suite = MemoryOptimizationSuite::new(config);
let data = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
let correlation = suite
.optimized_correlation_matrix(data.view())
.expect("Operation failed");
assert_eq!(correlation.nrows(), 3);
assert_eq!(correlation.ncols(), 3);
for i in 0..3 {
assert!((correlation[[i, i]] - 1.0).abs() < 1e-10);
}
}
}