opendp 0.4.0

A library of differential privacy algorithms for the statistical analysis of sensitive private data.
Documentation
use crate::core::{Domain, Measurement, Metric, PrivacyRelation, Measure};
use crate::dist::{SmoothedMaxDivergence, MaxDivergence};
use crate::dom::SizedDomain;
use crate::error::Fallible;
use num::Float;
use crate::traits::ExactIntCast;
use std::ops::Div;

pub trait IsSizedDomain: Domain { fn get_size(&self) -> Fallible<usize>; }
impl<D: Domain> IsSizedDomain for SizedDomain<D> {
    fn get_size(&self) -> Fallible<usize> { Ok(self.size) }
}

pub trait AmplifiableMeasure: Measure {
    fn amplify(&self, budget: &Self::Distance, population_size: usize, sample_size: usize) -> Fallible<Self::Distance>;
}

impl<Q> AmplifiableMeasure for MaxDivergence<Q>
    where Q: ExactIntCast<usize> + Div<Output=Q> + Float {
    fn amplify(&self, epsilon: &Q, population_size: usize, sample_size: usize) -> Fallible<Q> {
        let sampling_rate = Q::exact_int_cast(sample_size)? / Q::exact_int_cast(population_size)?;
        Ok((epsilon.exp_m1() / sampling_rate).ln_1p())
    }
}
impl<Q> AmplifiableMeasure for SmoothedMaxDivergence<Q>
    where Q: ExactIntCast<usize> + Div<Output=Q> + Float {
    fn amplify(&self, (epsilon, delta): &(Q, Q), population_size: usize, sample_size: usize) -> Fallible<(Q, Q)> {
        let sampling_rate = Q::exact_int_cast(sample_size)? / Q::exact_int_cast(population_size)?;
        Ok(((epsilon.exp_m1() / sampling_rate).ln_1p(), *delta / sampling_rate))
    }
}

pub fn make_population_amplification<DIA, DO, MI, MO>(
    measurement: &Measurement<DIA, DO, MI, MO>,
    population_size: usize,
) -> Fallible<Measurement<DIA, DO, MI, MO>>
    where DIA: IsSizedDomain,
          DO: Domain,
          MI: 'static + Metric,
          MO: 'static + AmplifiableMeasure {
    let mut measurement = measurement.clone();
    let sample_size = measurement.input_domain.get_size()?;
    if population_size < sample_size { 
        return fallible!(MakeMeasurement, "population size cannot be less than sample size") 
    }

    let privacy_relation = measurement.privacy_relation;
    let output_measure: MO = measurement.output_measure.clone();

    measurement.privacy_relation = PrivacyRelation::new_fallible(
        move |d_in, d_out: &MO::Distance| privacy_relation.eval(
            d_in, &output_measure.amplify(d_out, population_size, sample_size)?));

    Ok(measurement)
}

#[cfg(test)]
mod test {
    use crate::error::Fallible;
    use crate::trans::make_sized_bounded_mean;
    use crate::meas::make_base_laplace;
    use crate::comb::make_population_amplification;

    #[test]
    fn test_amplifier() -> Fallible<()> {
        let meas = (make_sized_bounded_mean(10, (0., 10.))? >> make_base_laplace(0.5)?)?;
        let amp = make_population_amplification(&meas, 100)?;
        amp.function.eval(&vec![1.; 10])?;
        assert!(meas.privacy_relation.eval(&2, &2.)?);
        assert!(!meas.privacy_relation.eval(&2, &1.999)?);
        assert!(amp.privacy_relation.eval(&2, &0.4941)?);
        assert!(!amp.privacy_relation.eval(&2, &0.494)?);
        Ok(())
    }
}