Skip to main content

entrenar/optim/dp/
gradient.rs

1//! Gradient operations for differential privacy.
2
3use rand::Rng;
4use std::f64::consts::PI;
5
6/// Clip gradient to max norm (per-sample)
7pub fn clip_gradient(grad: &[f64], max_norm: f64) -> Vec<f64> {
8    let norm: f64 = grad.iter().map(|x| x.powi(2)).sum::<f64>().sqrt();
9
10    if norm > max_norm {
11        let scale = max_norm / norm;
12        grad.iter().map(|x| x * scale).collect()
13    } else {
14        grad.to_vec()
15    }
16}
17
18/// Compute L2 norm of gradient
19pub fn grad_norm(grad: &[f64]) -> f64 {
20    grad.iter().map(|x| x.powi(2)).sum::<f64>().sqrt()
21}
22
23/// Add Gaussian noise to gradient
24pub fn add_gaussian_noise<R: Rng>(grad: &[f64], std_dev: f64, rng: &mut R) -> Vec<f64> {
25    grad.iter()
26        .map(|&x| {
27            // Box-Muller transform for Gaussian noise
28            let u1: f64 = rng.random::<f64>().max(1e-10);
29            let u2: f64 = rng.random::<f64>();
30            let noise = (-2.0 * u1.ln()).sqrt() * (2.0 * PI * u2).cos() * std_dev;
31            x + noise
32        })
33        .collect()
34}