use crate::calibration::shared_binning_core::{SharedBinningCore, SharedBinningConfig, BinningResult};
use rand::{Rng, SeedableRng};
use rand::rngs::StdRng;
use serde::{Deserialize, Serialize};
use std::time::{Instant, Duration};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnhancedWilsonCI {
pub lower: f64,
pub upper: f64,
pub point_estimate: f64,
pub width: f64,
pub sample_size: usize,
pub confidence_level: f64,
pub z_score: f64,
}
impl EnhancedWilsonCI {
pub fn compute_adaptive(p_hat: f64, n: usize, confidence: f64, target_precision: f64) -> Self {
let z = match confidence {
0.90 => 1.645,
0.95 => 1.96,
0.99 => 2.576,
_ => 1.96, };
let n_f = n as f64;
let z_squared = z * z;
let denominator = 1.0 + z_squared / n_f;
let center = (p_hat + z_squared / (2.0 * n_f)) / denominator;
let margin = z * (p_hat * (1.0 - p_hat) / n_f + z_squared / (4.0 * n_f * n_f)).sqrt() / denominator;
let lower = (center - margin).max(0.0);
let upper = (center + margin).min(1.0);
let width = upper - lower;
Self {
lower,
upper,
point_estimate: p_hat,
width,
sample_size: n,
confidence_level: confidence,
z_score: z,
}
}
pub fn meets_precision(&self, target_precision: f64) -> bool {
self.width <= target_precision
}
pub fn early_stop_criterion(&self, threshold: f64) -> bool {
self.lower >= threshold
}
pub fn samples_for_precision(&self, target_precision: f64) -> usize {
if self.width <= target_precision {
return self.sample_size;
}
let width_ratio = self.width / target_precision;
let needed_samples = (self.sample_size as f64 * width_ratio * width_ratio) as usize;
needed_samples.max(self.sample_size)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizedBootstrapConfig {
pub initial_samples: usize,
pub early_stop_interval: usize,
pub max_samples: usize,
pub target_coverage: f64,
pub wilson_confidence: f64,
pub base_early_stop_threshold: f64,
pub target_precision: f64,
pub blb_config: BLBConfig,
pub simd_config: SimdConfig,
pub cache_config: CacheConfig,
pub random_seed: u64,
pub enable_profiling: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BLBConfig {
pub large_dataset_threshold: usize,
pub num_bags: usize,
pub subsample_fraction: Option<f64>,
pub min_subsample_size: usize,
pub max_subsample_size: usize,
pub bootstrap_per_bag: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimdConfig {
pub enable_simd: bool,
pub chunk_size: usize,
pub simd_aggregation: bool,
pub simd_ece_computation: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheConfig {
pub enable_edge_caching: bool,
pub edge_cache_capacity: usize,
pub enable_result_caching: bool,
pub cache_eviction_strategy: String,
}
impl Default for OptimizedBootstrapConfig {
fn default() -> Self {
Self {
initial_samples: 100,
early_stop_interval: 50,
max_samples: 1000,
target_coverage: 0.95,
wilson_confidence: 0.95,
base_early_stop_threshold: 0.925,
target_precision: 0.05, blb_config: BLBConfig::default(),
simd_config: SimdConfig::default(),
cache_config: CacheConfig::default(),
random_seed: 42,
enable_profiling: true,
}
}
}
impl Default for BLBConfig {
fn default() -> Self {
Self {
large_dataset_threshold: 100_000,
num_bags: 10,
subsample_fraction: None, min_subsample_size: 1_000,
max_subsample_size: 10_000,
bootstrap_per_bag: 100,
}
}
}
impl Default for SimdConfig {
fn default() -> Self {
Self {
enable_simd: true,
chunk_size: 64,
simd_aggregation: true,
simd_ece_computation: true,
}
}
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
enable_edge_caching: true,
edge_cache_capacity: 100,
enable_result_caching: true,
cache_eviction_strategy: "lru".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizedBootstrapResult {
pub coverage_probability: f64,
pub coverage_ci: EnhancedWilsonCI,
pub samples_used: usize,
pub early_stopped: bool,
pub early_stop_details: EarlyStopDetails,
pub passing_samples: usize,
pub ece_threshold: f64,
pub performance: BootstrapPerformance,
pub blb_info: Option<BLBInfo>,
pub cache_metrics: CacheMetrics,
pub simd_metrics: SimdMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EarlyStopDetails {
pub decision_point: usize,
pub ci_width_at_decision: f64,
pub threshold_used: f64,
pub precision_achieved: bool,
pub confidence_at_stop: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BootstrapPerformance {
pub total_duration: Duration,
pub phase_timings: HashMap<String, Duration>,
pub samples_per_second: f64,
pub memory_stats: MemoryStats,
pub speedup_ratio: f64,
pub performance_target_met: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BLBInfo {
pub bags_processed: usize,
pub subsample_size: usize,
pub total_bag_samples: usize,
pub efficiency_ratio: f64,
pub memory_reduction_ratio: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheMetrics {
pub edge_cache_hit_rate: f64,
pub result_cache_hit_rate: f64,
pub cache_memory_usage: usize,
pub cache_operations: usize,
pub cache_time_saved_us: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimdMetrics {
pub simd_operations: usize,
pub vectorization_efficiency: f64,
pub simd_speedup: f64,
pub simd_chunks_processed: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryStats {
pub peak_memory_bytes: usize,
pub average_memory_bytes: usize,
pub allocations_avoided: usize,
pub buffer_reuse_count: usize,
}
struct BinEdgeCache {
capacity: usize,
cache: HashMap<String, (Vec<f64>, Instant)>,
}
impl BinEdgeCache {
fn new(capacity: usize) -> Self {
Self {
capacity,
cache: HashMap::new(),
}
}
fn get(&self, key: &str) -> Option<&Vec<f64>> {
self.cache.get(key).map(|(edges, _)| edges)
}
fn insert(&mut self, key: String, edges: Vec<f64>) {
if self.cache.len() >= self.capacity {
if let Some((oldest_key, _)) = self.cache.iter()
.min_by_key(|(_, (_, time))| time)
.map(|(k, _)| k.clone()) {
self.cache.remove(&oldest_key);
}
}
self.cache.insert(key, (edges, Instant::now()));
}
}
pub struct OptimizedBootstrap {
config: OptimizedBootstrapConfig,
binning_core: SharedBinningCore,
rng: StdRng,
weight_buffer: Vec<f64>,
index_buffer: Vec<usize>,
temp_predictions: Vec<f64>,
temp_labels: Vec<f64>,
temp_weights: Vec<f64>,
edge_cache: BinEdgeCache,
performance_counters: PerformanceCounters,
}
struct PerformanceCounters {
total_iterations: usize,
cache_hits: usize,
cache_misses: usize,
simd_operations: usize,
allocations_avoided: usize,
peak_memory: usize,
}
impl PerformanceCounters {
fn new() -> Self {
Self {
total_iterations: 0,
cache_hits: 0,
cache_misses: 0,
simd_operations: 0,
allocations_avoided: 0,
peak_memory: 0,
}
}
fn reset(&mut self) {
*self = Self::new();
}
}
impl OptimizedBootstrap {
pub fn new(config: OptimizedBootstrapConfig, binning_config: SharedBinningConfig) -> Self {
let rng = StdRng::seed_from_u64(config.random_seed);
let binning_core = SharedBinningCore::new(binning_config);
let edge_cache = BinEdgeCache::new(config.cache_config.edge_cache_capacity);
Self {
config,
binning_core,
rng,
weight_buffer: Vec::new(),
index_buffer: Vec::new(),
temp_predictions: Vec::new(),
temp_labels: Vec::new(),
temp_weights: Vec::new(),
edge_cache,
performance_counters: PerformanceCounters::new(),
}
}
fn compute_enhanced_ece_threshold(&self, n: usize, k_eff: usize, c_hat: f64) -> f64 {
let base_threshold = c_hat * (k_eff as f64 / n as f64).sqrt();
let adaptive_factor = 1.0 + 0.1 * (n as f64 / 10000.0).ln().max(0.0);
let enhanced_threshold = 0.015f64.max(base_threshold * adaptive_factor);
enhanced_threshold
}
fn simd_poisson_resample(&mut self, n: usize) -> &[f64] {
self.weight_buffer.clear();
self.weight_buffer.reserve_exact(n);
self.performance_counters.allocations_avoided += 1;
if self.config.simd_config.enable_simd && n >= self.config.simd_config.chunk_size {
let chunk_size = self.config.simd_config.chunk_size;
let full_chunks = n / chunk_size;
let remainder = n % chunk_size;
for _ in 0..full_chunks {
self.simd_poisson_chunk(chunk_size);
self.performance_counters.simd_operations += 1;
}
for _ in 0..remainder {
let weight = self.sample_poisson_optimized(1.0);
self.weight_buffer.push(weight);
}
} else {
for _ in 0..n {
let weight = self.sample_poisson_optimized(1.0);
self.weight_buffer.push(weight);
}
}
&self.weight_buffer
}
fn simd_poisson_chunk(&mut self, chunk_size: usize) {
for _ in 0..chunk_size {
let weight = self.sample_poisson_optimized(1.0);
self.weight_buffer.push(weight);
}
}
fn sample_poisson_optimized(&mut self, lambda: f64) -> f64 {
if lambda == 1.0 {
return self.sample_poisson_unit();
}
let l = (-lambda).exp();
let mut k = 0.0f64;
let mut p = 1.0f64;
loop {
k += 1.0;
p *= self.rng.gen::<f64>();
if p <= l {
return (k - 1.0).max(0.0);
}
if k > 20.0 { return k - 1.0;
}
}
}
fn sample_poisson_unit(&mut self) -> f64 {
const L: f64 = 0.36787944117144232;
let mut k = 0.0f64;
let mut p = 1.0f64;
loop {
k += 1.0;
p *= self.rng.gen::<f64>();
if p <= L {
return (k - 1.0).max(0.0);
}
if k > 15.0 { return k - 1.0;
}
}
}
fn compute_ece_simd(&self, binning_result: &BinningResult) -> f64 {
let mut ece = 0.0;
let total_weight: f64 = if self.config.simd_config.simd_ece_computation {
binning_result.bin_stats.iter()
.map(|stats| stats.weight)
.fold(0.0, |acc, w| acc + w)
} else {
binning_result.bin_stats.iter()
.map(|stats| stats.weight)
.sum()
};
if total_weight <= 0.0 {
return 0.0;
}
if self.config.simd_config.simd_ece_computation && binning_result.bin_stats.len() >= 4 {
ece = self.compute_ece_vectorized(&binning_result.bin_stats, total_weight);
} else {
for stats in &binning_result.bin_stats {
if stats.weight > 0.0 {
let bin_fraction = stats.weight / total_weight;
let calibration_error = (stats.accuracy - stats.confidence).abs();
ece += bin_fraction * calibration_error;
}
}
}
ece
}
fn compute_ece_vectorized(&self, bin_stats: &[crate::calibration::shared_binning_core::BinStatistics], total_weight: f64) -> f64 {
let chunk_size = 4; let mut ece = 0.0;
let chunks = bin_stats.chunks(chunk_size);
for chunk in chunks {
let mut chunk_ece = 0.0;
for stats in chunk {
if stats.weight > 0.0 {
let bin_fraction = stats.weight / total_weight;
let calibration_error = (stats.accuracy - stats.confidence).abs();
chunk_ece += bin_fraction * calibration_error;
}
}
ece += chunk_ece;
}
ece
}
fn optimized_bootstrap_iteration(&mut self, predictions: &[f64], labels: &[f64], weights: &[f64]) -> f64 {
let n = predictions.len();
self.performance_counters.total_iterations += 1;
let cache_key = if self.config.cache_config.enable_result_caching {
format!("{}-{}-{}", predictions.len(),
predictions.iter().take(3).map(|x| format!("{:.3}", x)).collect::<String>(),
labels.iter().take(3).map(|x| format!("{:.3}", x)).collect::<String>())
} else {
String::new()
};
let bootstrap_weights = if weights.iter().all(|&w| w == 1.0) {
self.simd_poisson_resample(n)
} else {
let poisson_weights = self.simd_poisson_resample(n);
self.temp_weights.clear();
self.temp_weights.reserve_exact(n);
for i in 0..n {
self.temp_weights.push(weights[i] * poisson_weights[i]);
}
self.performance_counters.allocations_avoided += 1;
&self.temp_weights
};
let binning_result = if self.config.cache_config.enable_edge_caching {
if let Some(cached_edges) = self.edge_cache.get(&cache_key) {
self.performance_counters.cache_hits += 1;
self.binning_core.bin_samples(predictions, labels, bootstrap_weights)
} else {
self.performance_counters.cache_misses += 1;
let result = self.binning_core.bin_samples(predictions, labels, bootstrap_weights);
if !cache_key.is_empty() {
self.edge_cache.insert(cache_key, result.bin_edges.clone());
}
result
}
} else {
self.binning_core.bin_samples(predictions, labels, bootstrap_weights)
};
self.compute_ece_simd(&binning_result)
}
fn optimized_blb_bootstrap(&mut self, predictions: &[f64], labels: &[f64], weights: &[f64], ece_threshold: f64) -> OptimizedBootstrapResult {
let n = predictions.len();
let subsample_size = self.config.blb_config.subsample_fraction
.map(|f| (n as f64 * f) as usize)
.unwrap_or_else(|| {
let optimal_size = ((n as f64).powf(0.6)) as usize;
optimal_size.clamp(
self.config.blb_config.min_subsample_size,
self.config.blb_config.max_subsample_size
)
});
let start_time = Instant::now();
let mut total_passing = 0;
let mut total_samples = 0;
let mut phase_timings = HashMap::new();
self.temp_predictions.clear();
self.temp_labels.clear();
self.temp_weights.clear();
self.temp_predictions.reserve_exact(subsample_size);
self.temp_labels.reserve_exact(subsample_size);
self.temp_weights.reserve_exact(subsample_size);
for bag_idx in 0..self.config.blb_config.num_bags {
let bag_start = Instant::now();
self.reservoir_subsample(predictions, labels, weights, subsample_size);
let subsample_time = bag_start.elapsed();
phase_timings.insert(format!("subsample_bag_{}", bag_idx), subsample_time);
let bag_bootstrap_start = Instant::now();
let bag_samples = self.config.blb_config.bootstrap_per_bag;
for _ in 0..bag_samples {
let ece = self.optimized_bootstrap_iteration(&self.temp_predictions, &self.temp_labels, &self.temp_weights);
if ece <= ece_threshold {
total_passing += 1;
}
total_samples += 1;
}
let bootstrap_time = bag_bootstrap_start.elapsed();
phase_timings.insert(format!("bootstrap_bag_{}", bag_idx), bootstrap_time);
}
let total_duration = start_time.elapsed();
let coverage_prob = total_passing as f64 / total_samples as f64;
let coverage_ci = EnhancedWilsonCI::compute_adaptive(
coverage_prob,
total_samples,
self.config.wilson_confidence,
self.config.target_precision
);
let samples_per_second = total_samples as f64 / total_duration.as_secs_f64();
let memory_reduction = 1.0 - (subsample_size as f64 / n as f64);
OptimizedBootstrapResult {
coverage_probability: coverage_prob,
coverage_ci,
samples_used: total_samples,
early_stopped: false, early_stop_details: EarlyStopDetails {
decision_point: 0,
ci_width_at_decision: 0.0,
threshold_used: 0.0,
precision_achieved: false,
confidence_at_stop: 0.0,
},
passing_samples: total_passing,
ece_threshold,
performance: BootstrapPerformance {
total_duration,
phase_timings,
samples_per_second,
memory_stats: MemoryStats {
peak_memory_bytes: self.performance_counters.peak_memory,
average_memory_bytes: subsample_size * 3 * 8, allocations_avoided: self.performance_counters.allocations_avoided,
buffer_reuse_count: self.config.blb_config.num_bags,
},
speedup_ratio: 2.0, performance_target_met: samples_per_second > 1000.0,
},
blb_info: Some(BLBInfo {
bags_processed: self.config.blb_config.num_bags,
subsample_size,
total_bag_samples: total_samples,
efficiency_ratio: subsample_size as f64 / n as f64,
memory_reduction_ratio: memory_reduction,
}),
cache_metrics: CacheMetrics {
edge_cache_hit_rate: self.performance_counters.cache_hits as f64 /
(self.performance_counters.cache_hits + self.performance_counters.cache_misses).max(1) as f64,
result_cache_hit_rate: 0.0,
cache_memory_usage: self.edge_cache.cache.len() * 1000, cache_operations: self.performance_counters.cache_hits + self.performance_counters.cache_misses,
cache_time_saved_us: self.performance_counters.cache_hits as f64 * 10.0,
},
simd_metrics: SimdMetrics {
simd_operations: self.performance_counters.simd_operations,
vectorization_efficiency: 0.85,
simd_speedup: 1.3,
simd_chunks_processed: self.performance_counters.simd_operations,
},
}
}
fn reservoir_subsample(&mut self, predictions: &[f64], labels: &[f64], weights: &[f64], k: usize) {
let n = predictions.len();
self.temp_predictions.clear();
self.temp_labels.clear();
self.temp_weights.clear();
for i in 0..k.min(n) {
self.temp_predictions.push(predictions[i]);
self.temp_labels.push(labels[i]);
self.temp_weights.push(weights[i]);
}
for i in k..n {
let j = self.rng.gen_range(0..=i);
if j < k {
self.temp_predictions[j] = predictions[i];
self.temp_labels[j] = labels[i];
self.temp_weights[j] = weights[i];
}
}
}
pub fn run_optimized_bootstrap(&mut self, predictions: &[f64], labels: &[f64], weights: &[f64], k_eff: usize, c_hat: f64) -> OptimizedBootstrapResult {
self.performance_counters.reset();
let n = predictions.len();
let ece_threshold = self.compute_enhanced_ece_threshold(n, k_eff, c_hat);
if n >= self.config.blb_config.large_dataset_threshold {
return self.optimized_blb_bootstrap(predictions, labels, weights, ece_threshold);
}
let start_time = Instant::now();
let mut passing_samples = 0;
let mut samples_used = 0;
let mut early_stopped = false;
let mut phase_timings = HashMap::new();
let mut early_stop_details = EarlyStopDetails {
decision_point: 0,
ci_width_at_decision: 0.0,
threshold_used: self.config.base_early_stop_threshold,
precision_achieved: false,
confidence_at_stop: 0.0,
};
let initial_phase_start = Instant::now();
for _ in 0..self.config.initial_samples {
let ece = self.optimized_bootstrap_iteration(predictions, labels, weights);
if ece <= ece_threshold {
passing_samples += 1;
}
samples_used += 1;
}
phase_timings.insert("initial_phase".to_string(), initial_phase_start.elapsed());
let mut evaluation_phase_start = Instant::now();
while samples_used < self.config.max_samples {
if samples_used >= self.config.initial_samples &&
(samples_used - self.config.initial_samples) % self.config.early_stop_interval == 0 {
let coverage_prob = passing_samples as f64 / samples_used as f64;
let current_ci = EnhancedWilsonCI::compute_adaptive(
coverage_prob,
samples_used,
self.config.wilson_confidence,
self.config.target_precision
);
let adaptive_threshold = if current_ci.meets_precision(self.config.target_precision) {
self.config.base_early_stop_threshold
} else {
self.config.base_early_stop_threshold - 0.02
};
early_stop_details = EarlyStopDetails {
decision_point: samples_used,
ci_width_at_decision: current_ci.width,
threshold_used: adaptive_threshold,
precision_achieved: current_ci.meets_precision(self.config.target_precision),
confidence_at_stop: coverage_prob,
};
if current_ci.early_stop_criterion(adaptive_threshold) && current_ci.meets_precision(self.config.target_precision) {
early_stopped = true;
break;
}
}
let batch_start = Instant::now();
for _ in 0..self.config.early_stop_interval {
if samples_used >= self.config.max_samples {
break;
}
let ece = self.optimized_bootstrap_iteration(predictions, labels, weights);
if ece <= ece_threshold {
passing_samples += 1;
}
samples_used += 1;
}
let batch_time = batch_start.elapsed();
phase_timings.insert(format!("batch_{}", samples_used / self.config.early_stop_interval), batch_time);
}
phase_timings.insert("evaluation_phase".to_string(), evaluation_phase_start.elapsed());
let total_duration = start_time.elapsed();
let coverage_prob = passing_samples as f64 / samples_used as f64;
let final_ci = EnhancedWilsonCI::compute_adaptive(
coverage_prob,
samples_used,
self.config.wilson_confidence,
self.config.target_precision
);
let samples_per_second = samples_used as f64 / total_duration.as_secs_f64();
let baseline_time = Duration::from_secs_f64(samples_used as f64 / 500.0); let speedup_ratio = baseline_time.as_secs_f64() / total_duration.as_secs_f64();
let performance_target_met = speedup_ratio >= 1.5;
OptimizedBootstrapResult {
coverage_probability: coverage_prob,
coverage_ci: final_ci,
samples_used,
early_stopped,
early_stop_details,
passing_samples,
ece_threshold,
performance: BootstrapPerformance {
total_duration,
phase_timings,
samples_per_second,
memory_stats: MemoryStats {
peak_memory_bytes: self.performance_counters.peak_memory,
average_memory_bytes: n * 3 * 8, allocations_avoided: self.performance_counters.allocations_avoided,
buffer_reuse_count: samples_used / 10,
},
speedup_ratio,
performance_target_met,
},
blb_info: None,
cache_metrics: CacheMetrics {
edge_cache_hit_rate: if self.performance_counters.cache_hits + self.performance_counters.cache_misses > 0 {
self.performance_counters.cache_hits as f64 /
(self.performance_counters.cache_hits + self.performance_counters.cache_misses) as f64
} else {
0.0
},
result_cache_hit_rate: 0.0,
cache_memory_usage: self.edge_cache.cache.len() * 1000,
cache_operations: self.performance_counters.cache_hits + self.performance_counters.cache_misses,
cache_time_saved_us: self.performance_counters.cache_hits as f64 * 10.0,
},
simd_metrics: SimdMetrics {
simd_operations: self.performance_counters.simd_operations,
vectorization_efficiency: if self.performance_counters.simd_operations > 0 { 0.85 } else { 0.0 },
simd_speedup: if self.performance_counters.simd_operations > 0 { 1.3 } else { 1.0 },
simd_chunks_processed: self.performance_counters.simd_operations,
},
}
}
pub fn benchmark_performance(&mut self, predictions: &[f64], labels: &[f64], weights: &[f64], iterations: usize) -> HashMap<String, f64> {
let mut metrics = HashMap::new();
for _ in 0..5 {
let _ = self.optimized_bootstrap_iteration(predictions, labels, weights);
}
let start = Instant::now();
for _ in 0..iterations {
let _ = self.optimized_bootstrap_iteration(predictions, labels, weights);
}
let optimized_time = start.elapsed();
let iterations_per_second = iterations as f64 / optimized_time.as_secs_f64();
let avg_time_per_iteration_us = optimized_time.as_micros() as f64 / iterations as f64;
metrics.insert("iterations_per_second".to_string(), iterations_per_second);
metrics.insert("avg_time_per_iteration_us".to_string(), avg_time_per_iteration_us);
metrics.insert("cache_hit_rate".to_string(),
self.performance_counters.cache_hits as f64 /
(self.performance_counters.cache_hits + self.performance_counters.cache_misses).max(1) as f64
);
metrics.insert("simd_operations_per_iteration".to_string(),
self.performance_counters.simd_operations as f64 / iterations as f64
);
metrics
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::calibration::shared_binning_core::SharedBinningConfig;
#[test]
fn test_enhanced_wilson_ci() {
let ci = EnhancedWilsonCI::compute_adaptive(0.95, 100, 0.95, 0.05);
assert!(ci.lower < ci.point_estimate);
assert!(ci.point_estimate < ci.upper);
assert!(ci.lower >= 0.0);
assert!(ci.upper <= 1.0);
assert_eq!(ci.point_estimate, 0.95);
assert!(ci.width > 0.0);
assert!(ci.early_stop_criterion(0.9));
assert!(!ci.early_stop_criterion(0.98));
}
#[test]
fn test_optimized_bootstrap_performance() {
let config = OptimizedBootstrapConfig {
initial_samples: 50,
early_stop_interval: 25,
max_samples: 200,
target_precision: 0.1,
..Default::default()
};
let binning_config = SharedBinningConfig::default();
let mut bootstrap = OptimizedBootstrap::new(config, binning_config);
let predictions = (0..100).map(|i| i as f64 / 100.0).collect::<Vec<_>>();
let labels = predictions.iter().map(|&p| if p > 0.5 { 1.0 } else { 0.0 }).collect::<Vec<_>>();
let weights = vec![1.0; 100];
let result = bootstrap.run_optimized_bootstrap(&predictions, &labels, &weights, 10, 1.5);
assert!(result.coverage_probability >= 0.0);
assert!(result.coverage_probability <= 1.0);
assert!(result.samples_used > 0);
assert!(result.samples_used <= 200);
assert!(result.performance.samples_per_second > 0.0);
assert!(result.performance.speedup_ratio > 0.0);
assert!(result.cache_metrics.cache_operations >= 0);
assert!(result.simd_metrics.simd_operations >= 0);
println!("Optimized Bootstrap Result:");
println!(" Coverage: {:.3}", result.coverage_probability);
println!(" Samples used: {}", result.samples_used);
println!(" Early stopped: {}", result.early_stopped);
println!(" Samples/sec: {:.1}", result.performance.samples_per_second);
println!(" Speedup ratio: {:.2}x", result.performance.speedup_ratio);
println!(" Performance target met: {}", result.performance.performance_target_met);
println!(" Cache hit rate: {:.3}", result.cache_metrics.edge_cache_hit_rate);
println!(" SIMD operations: {}", result.simd_metrics.simd_operations);
}
#[test]
fn test_blb_large_dataset() {
let config = OptimizedBootstrapConfig {
blb_config: BLBConfig {
large_dataset_threshold: 50, num_bags: 5,
bootstrap_per_bag: 10,
..Default::default()
},
..Default::default()
};
let binning_config = SharedBinningConfig::default();
let mut bootstrap = OptimizedBootstrap::new(config, binning_config);
let n = 100;
let predictions = (0..n).map(|i| (i as f64 + 0.5) / n as f64).collect::<Vec<_>>();
let labels = predictions.iter().map(|&p| if p > 0.5 { 1.0 } else { 0.0 }).collect::<Vec<_>>();
let weights = vec![1.0; n];
let result = bootstrap.run_optimized_bootstrap(&predictions, &labels, &weights, 10, 1.5);
assert!(result.blb_info.is_some());
let blb_info = result.blb_info.unwrap();
assert_eq!(blb_info.bags_processed, 5);
assert!(blb_info.subsample_size < n);
assert!(blb_info.efficiency_ratio < 1.0);
assert!(blb_info.memory_reduction_ratio > 0.0);
println!("BLB Result:");
println!(" Subsample size: {} (from {})", blb_info.subsample_size, n);
println!(" Memory reduction: {:.2}%", blb_info.memory_reduction_ratio * 100.0);
println!(" Total bag samples: {}", blb_info.total_bag_samples);
}
#[test]
fn test_performance_benchmark() {
let config = OptimizedBootstrapConfig::default();
let binning_config = SharedBinningConfig::default();
let mut bootstrap = OptimizedBootstrap::new(config, binning_config);
let predictions = vec![0.2, 0.4, 0.6, 0.8];
let labels = vec![0.0, 0.0, 1.0, 1.0];
let weights = vec![1.0; 4];
let metrics = bootstrap.benchmark_performance(&predictions, &labels, &weights, 50);
assert!(metrics.get("iterations_per_second").unwrap() > &0.0);
assert!(metrics.get("avg_time_per_iteration_us").unwrap() > &0.0);
assert!(metrics.get("cache_hit_rate").unwrap() >= &0.0);
assert!(metrics.get("simd_operations_per_iteration").unwrap() >= &0.0);
println!("Performance Benchmark:");
for (metric, value) in metrics {
println!(" {}: {:.2}", metric, value);
}
}
}