entrenar/optim/dp/
accountant.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct RdpAccountant {
14 orders: Vec<f64>,
16 pub(crate) rdp: Vec<f64>,
18 steps: usize,
20}
21
22impl RdpAccountant {
23 pub fn new() -> Self {
25 let orders: Vec<f64> = (2..=256).map(f64::from).collect();
27 let rdp = vec![0.0; orders.len()];
28 Self { orders, rdp, steps: 0 }
29 }
30
31 pub fn with_orders(orders: Vec<f64>) -> Self {
33 let rdp = vec![0.0; orders.len()];
34 Self { orders, rdp, steps: 0 }
35 }
36
37 pub fn step(&mut self, noise_multiplier: f64, sample_rate: f64) {
39 for (i, &alpha) in self.orders.iter().enumerate() {
40 let rdp_step = compute_rdp_gaussian(noise_multiplier, sample_rate, alpha);
41 self.rdp[i] += rdp_step;
42 }
43 self.steps += 1;
44 }
45
46 pub fn get_privacy_spent(&self, delta: f64) -> (f64, f64) {
48 rdp_to_dp(&self.orders, &self.rdp, delta)
49 }
50
51 pub fn n_steps(&self) -> usize {
53 self.steps
54 }
55
56 pub fn reset(&mut self) {
58 for r in &mut self.rdp {
59 *r = 0.0;
60 }
61 self.steps = 0;
62 }
63}
64
65impl Default for RdpAccountant {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71pub fn compute_rdp_gaussian(noise_multiplier: f64, sample_rate: f64, alpha: f64) -> f64 {
73 if noise_multiplier <= 0.0 || sample_rate <= 0.0 {
74 return f64::INFINITY;
75 }
76
77 let sigma = noise_multiplier;
80
81 if sample_rate >= 1.0 {
82 alpha / (2.0 * sigma.powi(2))
84 } else {
85 let q = sample_rate;
89
90 if alpha <= 1.0 {
92 return f64::INFINITY;
93 }
94
95 let log_a = (alpha - 1.0) * ((alpha * q.powi(2)) / (2.0 * sigma.powi(2))).min(1.0).ln_1p();
97 log_a / (alpha - 1.0)
98 }
99}
100
101pub fn rdp_to_dp(orders: &[f64], rdp: &[f64], delta: f64) -> (f64, f64) {
103 if delta <= 0.0 || orders.is_empty() {
104 return (f64::INFINITY, delta);
105 }
106
107 let log_delta = delta.max(f64::MIN_POSITIVE).ln();
108
109 let mut min_epsilon = f64::INFINITY;
111 for (&alpha, &rdp_alpha) in orders.iter().zip(rdp.iter()) {
112 if alpha <= 1.0 {
113 continue;
114 }
115 let epsilon = rdp_alpha
117 + (1.0 / (alpha - 1.0)) * ((alpha - 1.0) / alpha).max(f64::MIN_POSITIVE).ln()
118 - (log_delta + (alpha - 1.0).max(f64::MIN_POSITIVE).ln()) / (alpha - 1.0);
119
120 if epsilon < min_epsilon && epsilon >= 0.0 {
121 min_epsilon = epsilon;
122 }
123 }
124
125 (min_epsilon, delta)
126}