use std::fmt::Debug;
use std::ops::{Mul, Sub};
use super::*;
use crate::domains::AtomDomain;
use crate::error::ExplainUnwrap;
use crate::measurements::{make_gaussian, make_laplace};
use crate::measures::{MaxDivergence, ZeroConcentratedDivergence};
use crate::metrics::AbsoluteDistance;
#[test]
fn test_comparison() -> Fallible<()> {
let alpha = 0.05;
let scale = 20.;
let accuracy = 20.;
let c_acc = laplacian_scale_to_accuracy(scale, alpha)?;
let d_acc = discrete_laplacian_scale_to_accuracy(scale, alpha)?;
assert!(c_acc < d_acc);
println!("lap cont accuracy: {}", c_acc);
println!("lap disc accuracy: {}", d_acc);
let c_scale = accuracy_to_laplacian_scale(accuracy, alpha)?;
let d_scale = accuracy_to_discrete_laplacian_scale(accuracy, alpha)?;
assert!(c_scale > d_scale);
println!("lap cont scale: {}", c_scale);
println!("lap disc scale: {}", d_scale);
let c_acc = gaussian_scale_to_accuracy(scale, alpha)?;
let d_acc = discrete_gaussian_scale_to_accuracy(scale, alpha)?;
assert!(c_acc < d_acc);
println!("gauss cont accuracy: {}", c_acc);
println!("gauss disc accuracy: {}", d_acc);
let c_scale = accuracy_to_gaussian_scale(accuracy, alpha)?;
let d_scale = accuracy_to_discrete_gaussian_scale(accuracy, alpha)?;
assert!(c_scale > d_scale);
println!("gauss cont scale: {}", c_scale);
println!("gauss disc scale: {}", d_scale);
Ok(())
}
fn print_statement<T: Copy + Debug + One + From<i8> + Sub<Output = T> + Mul<Output = T>>(
dist: &str,
scale: T,
accuracy: T,
alpha: T,
) {
let _100 = T::from(100);
println!(
"When the {dist} scale is {scale:?}, the DP estimate differs from the true value \
by no more than {accuracy:?} at a level-alpha of {alpha:?}, \
or with (1 - {alpha:?})100% = {perc:.2?}% confidence.",
dist = dist,
scale = scale,
accuracy = accuracy,
alpha = alpha,
perc = (T::one() - alpha) * _100
);
}
#[test]
fn test_laplacian_scale_to_accuracy() -> Fallible<()> {
macro_rules! check_laplacian_scale_to_accuracy {
(scale=$scale:literal, alpha=$alpha:literal) => {
print_statement(
"laplacian",
$scale,
laplacian_scale_to_accuracy($scale, $alpha)?,
$alpha,
)
};
}
check_laplacian_scale_to_accuracy!(scale = 1., alpha = 0.05);
check_laplacian_scale_to_accuracy!(scale = 2., alpha = 0.05);
check_laplacian_scale_to_accuracy!(scale = 0., alpha = 0.55);
Ok(())
}
#[test]
pub fn test_accuracy_to_laplacian_scale() -> Fallible<()> {
macro_rules! check_accuracy_to_laplacian_scale {
(accuracy=$accuracy:literal, alpha=$alpha:literal) => {
print_statement(
"laplacian",
accuracy_to_laplacian_scale($accuracy, $alpha)?,
$accuracy,
$alpha,
)
};
}
check_accuracy_to_laplacian_scale!(accuracy = 1., alpha = 0.05);
check_accuracy_to_laplacian_scale!(accuracy = 2., alpha = 0.05);
check_accuracy_to_laplacian_scale!(accuracy = 0.01, alpha = 0.1);
Ok(())
}
#[test]
pub fn test_gaussian_scale_to_accuracy() -> Fallible<()> {
macro_rules! check_gaussian_scale_to_accuracy {
(scale=$scale:literal, alpha=$alpha:literal) => {
print_statement(
"gaussian",
$scale,
gaussian_scale_to_accuracy($scale, $alpha)?,
$alpha,
)
};
}
check_gaussian_scale_to_accuracy!(scale = 1., alpha = 0.05);
check_gaussian_scale_to_accuracy!(scale = 2., alpha = 0.10);
check_gaussian_scale_to_accuracy!(scale = 3., alpha = 0.55);
Ok(())
}
#[test]
pub fn test_accuracy_to_gaussian_scale() -> Fallible<()> {
macro_rules! check_accuracy_to_gaussian_scale {
(accuracy=$accuracy:literal, alpha=$alpha:literal) => {
print_statement(
"gaussian",
accuracy_to_gaussian_scale($accuracy, $alpha)?,
$accuracy,
$alpha,
)
};
}
check_accuracy_to_gaussian_scale!(accuracy = 1., alpha = 0.05);
check_accuracy_to_gaussian_scale!(accuracy = 2., alpha = 0.05);
check_accuracy_to_gaussian_scale!(accuracy = 1.2, alpha = 0.1);
Ok(())
}
#[test]
fn test_relative_laplacian_scale_to_accuracy() -> Fallible<()> {
assert!(
laplacian_scale_to_accuracy(1., 0.05)? > laplacian_scale_to_accuracy(1., 0.06)?
);
assert!(
laplacian_scale_to_accuracy(2., 0.05)? > laplacian_scale_to_accuracy(1., 0.05)?
);
Ok(())
}
#[test]
pub fn test_relative_accuracy_to_laplacian_scale() -> Fallible<()> {
assert!(
accuracy_to_laplacian_scale(1., 0.05)? < accuracy_to_laplacian_scale(1., 0.06)?
);
assert!(
accuracy_to_laplacian_scale(2., 0.05)? > accuracy_to_laplacian_scale(1., 0.05)?
);
Ok(())
}
#[test]
pub fn test_relative_gaussian_scale_to_accuracy() -> Fallible<()> {
assert!(
gaussian_scale_to_accuracy(1., 0.05)? > gaussian_scale_to_accuracy(1., 0.06)?
);
assert!(
gaussian_scale_to_accuracy(2., 0.05)? > gaussian_scale_to_accuracy(1., 0.05)?
);
Ok(())
}
#[test]
pub fn test_relative_accuracy_to_gaussian_scale() -> Fallible<()> {
assert!(
accuracy_to_gaussian_scale(1., 0.05)? < accuracy_to_gaussian_scale(1., 0.06)?
);
assert!(
accuracy_to_gaussian_scale(2., 0.05)? > accuracy_to_gaussian_scale(1., 0.05)?
); Ok(())
}
#[test]
pub fn test_empirical_laplace_accuracy() -> Fallible<()> {
let accuracy = 1.0;
let theoretical_alpha = 0.05;
let scale = accuracy_to_laplacian_scale(accuracy, theoretical_alpha)?;
let input_domain = AtomDomain::<f64>::new_non_nan();
let input_metric = AbsoluteDistance::<i32>::default();
let laplace =
make_laplace::<_, _, MaxDivergence>(input_domain, input_metric, scale, Some(-100))?;
let n = 50_000;
let empirical_alpha = (0..n)
.filter(|_| laplace.invoke(&0.0).unwrap().abs() > accuracy)
.count() as f64
/ n as f64;
println!("Laplacian significance levels/alpha");
println!("Theoretical: {:?}", theoretical_alpha);
println!("Empirical: {:?}", empirical_alpha);
assert!((empirical_alpha - theoretical_alpha).abs() < 1e-2);
Ok(())
}
#[test]
pub fn test_empirical_gaussian_accuracy() -> Fallible<()> {
let accuracy = 1.0;
let theoretical_alpha = 0.05;
let scale = accuracy_to_gaussian_scale(accuracy, theoretical_alpha)?;
let base_gaussian = make_gaussian::<_, _, ZeroConcentratedDivergence>(
AtomDomain::<f64>::new_non_nan(),
AbsoluteDistance::<u32>::default(),
scale,
Some(-100),
)?;
let n = 50_000;
let empirical_alpha = (0..n)
.filter(|_| base_gaussian.invoke(&0.0).unwrap_test().abs() > accuracy)
.count() as f64
/ n as f64;
println!("Gaussian significance levels/alpha");
println!("Theoretical: {:?}", theoretical_alpha);
println!("Empirical: {:?}", empirical_alpha);
assert!((empirical_alpha - theoretical_alpha).abs() < 1e-2);
Ok(())
}
#[test]
pub fn test_empirical_discrete_laplace_accuracy() -> Fallible<()> {
let accuracy = 25;
let theoretical_alpha = 0.05;
let scale = accuracy_to_discrete_laplacian_scale(accuracy as f64, theoretical_alpha)?;
println!("scale: {scale}");
let input_domain = AtomDomain::<i32>::default();
let input_metric = AbsoluteDistance::<i32>::default();
let base_dl = make_laplace::<_, _, MaxDivergence>(input_domain, input_metric, scale, None)?;
let n = 50_000;
let empirical_alpha = (0..n)
.filter(|_| base_dl.invoke(&0).unwrap().clamp(-127, 127).abs() >= accuracy)
.count() as f64
/ n as f64;
println!("Discrete laplace significance levels/alpha");
println!("Theoretical: {:?}", theoretical_alpha);
println!("Empirical: {:?}", empirical_alpha);
assert!((empirical_alpha - theoretical_alpha).abs() < 1e-2);
Ok(())
}
#[test]
pub fn test_empirical_discrete_gaussian_accuracy() -> Fallible<()> {
let accuracy = 25;
let theoretical_alpha = 0.05;
let scale = accuracy_to_discrete_gaussian_scale(accuracy as f64, theoretical_alpha)?;
println!("scale: {}", scale);
let base_dg = make_gaussian::<_, _, ZeroConcentratedDivergence>(
AtomDomain::<i8>::default(),
AbsoluteDistance::<i32>::default(),
scale,
None,
)?;
let n = 50_000;
let empirical_alpha = (0..n)
.filter(|_| base_dg.invoke(&0).unwrap().clamp(-127, 127).abs() >= accuracy)
.count() as f64
/ n as f64;
println!("Discrete gaussian significance levels/alpha");
println!("Theoretical: {:?}", theoretical_alpha);
println!("Empirical: {:?}", empirical_alpha);
assert!((empirical_alpha - theoretical_alpha).abs() < 1e-2);
Ok(())
}
#[test]
pub fn test_roundtrip() -> Fallible<()> {
let accuracy = 1.;
let alpha = 0.05;
let accuracy_2 =
gaussian_scale_to_accuracy(accuracy_to_gaussian_scale(accuracy, alpha)?, alpha)?;
assert!((accuracy - accuracy_2).abs() < 1e-8);
let accuracy_2 =
laplacian_scale_to_accuracy(accuracy_to_laplacian_scale(accuracy, alpha)?, alpha)?;
assert!((accuracy - accuracy_2).abs() < 1e-8);
Ok(())
}