use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RdpAccountant {
orders: Vec<f64>,
pub(crate) rdp: Vec<f64>,
steps: usize,
}
impl RdpAccountant {
pub fn new() -> Self {
let orders: Vec<f64> = (2..=256).map(f64::from).collect();
let rdp = vec![0.0; orders.len()];
Self { orders, rdp, steps: 0 }
}
pub fn with_orders(orders: Vec<f64>) -> Self {
let rdp = vec![0.0; orders.len()];
Self { orders, rdp, steps: 0 }
}
pub fn step(&mut self, noise_multiplier: f64, sample_rate: f64) {
for (i, &alpha) in self.orders.iter().enumerate() {
let rdp_step = compute_rdp_gaussian(noise_multiplier, sample_rate, alpha);
self.rdp[i] += rdp_step;
}
self.steps += 1;
}
pub fn get_privacy_spent(&self, delta: f64) -> (f64, f64) {
rdp_to_dp(&self.orders, &self.rdp, delta)
}
pub fn n_steps(&self) -> usize {
self.steps
}
pub fn reset(&mut self) {
for r in &mut self.rdp {
*r = 0.0;
}
self.steps = 0;
}
}
impl Default for RdpAccountant {
fn default() -> Self {
Self::new()
}
}
pub fn compute_rdp_gaussian(noise_multiplier: f64, sample_rate: f64, alpha: f64) -> f64 {
if noise_multiplier <= 0.0 || sample_rate <= 0.0 {
return f64::INFINITY;
}
let sigma = noise_multiplier;
if sample_rate >= 1.0 {
alpha / (2.0 * sigma.powi(2))
} else {
let q = sample_rate;
if alpha <= 1.0 {
return f64::INFINITY;
}
let log_a = (alpha - 1.0) * ((alpha * q.powi(2)) / (2.0 * sigma.powi(2))).min(1.0).ln_1p();
log_a / (alpha - 1.0)
}
}
pub fn rdp_to_dp(orders: &[f64], rdp: &[f64], delta: f64) -> (f64, f64) {
if delta <= 0.0 || orders.is_empty() {
return (f64::INFINITY, delta);
}
let log_delta = delta.max(f64::MIN_POSITIVE).ln();
let mut min_epsilon = f64::INFINITY;
for (&alpha, &rdp_alpha) in orders.iter().zip(rdp.iter()) {
if alpha <= 1.0 {
continue;
}
let epsilon = rdp_alpha
+ (1.0 / (alpha - 1.0)) * ((alpha - 1.0) / alpha).max(f64::MIN_POSITIVE).ln()
- (log_delta + (alpha - 1.0).max(f64::MIN_POSITIVE).ln()) / (alpha - 1.0);
if epsilon < min_epsilon && epsilon >= 0.0 {
min_epsilon = epsilon;
}
}
(min_epsilon, delta)
}