pub const DEFAULT_TOLERANCE: f64 = 1e-5;
#[inline]
pub fn quantize_threshold(value: f64) -> f32 {
value as f32
}
#[inline]
pub fn quantize_leaf(leaf_value: f64, learning_rate: f64) -> f32 {
(learning_rate * leaf_value) as f32
}
#[inline]
pub fn within_tolerance(value: f64, tolerance: f64) -> bool {
let quantized = value as f32;
let roundtrip = quantized as f64;
let diff = crate::math::abs(value - roundtrip);
diff <= tolerance
}
pub fn max_quantization_error(values: &[f64]) -> f64 {
values
.iter()
.map(|&v| {
let q = v as f32;
crate::math::abs(v - q as f64)
})
.fold(0.0f64, |a, b| if a > b { a } else { b })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn small_values_quantize_exactly() {
assert!(within_tolerance(0.0, DEFAULT_TOLERANCE));
assert!(within_tolerance(1.0, DEFAULT_TOLERANCE));
assert!(within_tolerance(-1.0, DEFAULT_TOLERANCE));
assert!(within_tolerance(0.5, DEFAULT_TOLERANCE));
}
#[test]
fn typical_thresholds_within_tolerance() {
let thresholds = [0.001, 0.1, 1.5, 10.0, 100.0, -0.5, -50.0];
for &t in &thresholds {
assert!(
within_tolerance(t, DEFAULT_TOLERANCE),
"threshold {} should be within tolerance",
t
);
}
}
#[test]
fn quantize_leaf_bakes_in_lr() {
let leaf = 2.0;
let lr = 0.1;
let q = quantize_leaf(leaf, lr);
assert!((q - 0.2f32).abs() < 1e-7);
}
#[test]
fn max_error_of_empty_slice() {
assert_eq!(max_quantization_error(&[]), 0.0);
}
#[test]
fn max_error_tracks_worst_case() {
let values = [0.0, 1.0, 0.1]; let err = max_quantization_error(&values);
assert!(err > 0.0);
assert!(err < 1e-7); }
}