Skip to main content

diff_priv/noise/laplace/
laplace_noiser.rs

1use crate::data_manipulation::aggregation::truncate_to_domain;
2use crate::data_manipulation::anonymizable::{
3    Anonymizable, IntervalType, NominalType, OrdinalType, QuasiIdentifierType, QuasiIdentifierTypes,
4};
5use crate::noise::laplace::categorical_noiser::CategoricalNoiser;
6use crate::noise::laplace::numerical_noiser::NumericalNoiser;
7use crate::noise::noiser::Noiser;
8
9/// location of laplace distribution (mu)
10pub const LOC: f64 = 0.0;
11
12/// possible noiser categories for the laplace noiser
13#[derive(Clone)]
14enum NoiserCategories {
15    NumericalNoiser(NumericalNoiser),
16    CategoricalNoiser(CategoricalNoiser),
17}
18
19/// QI types that support categorical noise
20#[derive(Clone)]
21pub enum CategoricalTypes {
22    Nominal(NominalType),
23    Ordinal(OrdinalType),
24}
25
26/// The laplace noice noiser used for introducing random noise to make the QI's
27/// differentially private
28#[derive(Default, Clone)]
29pub struct LaplaceNoiser {
30    eps: f64,                          // differential privacy parameter
31    k: usize,                          // k anonymity level
32    noise_thr: f64,                    // categorical noise threshold
33    qi_noisers: Vec<NoiserCategories>, // vector containing all the different noisers for the QI's
34}
35
36impl LaplaceNoiser {
37    pub fn new(eps: f64, k: usize, noise_thr: f64) -> Self {
38        Self {
39            eps,
40            k,
41            noise_thr,
42            ..Default::default()
43        }
44    }
45
46    /// generate and add noise to an interval QI type
47    fn generate_noise_interval(
48        &mut self,
49        interval: IntervalType,
50        qi_len: usize,
51        index: usize,
52    ) -> QuasiIdentifierTypes {
53        match self.qi_noisers.get_mut(index) {
54            None => {
55                let mut noiser =
56                    NumericalNoiser::initialize(self.eps, self.k, qi_len as f64, &interval);
57                let noise = noiser.generate_noise(&interval);
58                self.qi_noisers
59                    .push(NoiserCategories::NumericalNoiser(noiser));
60                QuasiIdentifierTypes::Interval(self.add_noise_interval(noise, interval))
61            }
62            Some(category) => match category {
63                NoiserCategories::NumericalNoiser(noiser) => {
64                    let noise = noiser.generate_noise(&interval);
65                    QuasiIdentifierTypes::Interval(self.add_noise_interval(noise, interval))
66                }
67                _ => panic!("wrong noiser type detected"),
68            },
69        }
70    }
71
72    /// generate and add noise to an ordinal QI type
73    fn generate_noise_ordinal(
74        &mut self,
75        ordinal: OrdinalType,
76        stream_weight: usize,
77        index: usize,
78    ) -> QuasiIdentifierTypes {
79        match self.qi_noisers.get_mut(index) {
80            None => {
81                let mut noiser = CategoricalNoiser::initialize(self.noise_thr, stream_weight);
82                let noise = noiser.generate_noise(CategoricalTypes::Ordinal(ordinal));
83                self.qi_noisers
84                    .push(NoiserCategories::CategoricalNoiser(noiser));
85                QuasiIdentifierTypes::Ordinal(self.add_noise_ordinal(noise, ordinal))
86            }
87            Some(category) => match category {
88                NoiserCategories::CategoricalNoiser(noiser) => {
89                    let noise = noiser.generate_noise(CategoricalTypes::Ordinal(ordinal));
90                    QuasiIdentifierTypes::Ordinal(self.add_noise_ordinal(noise, ordinal))
91                }
92                _ => panic!("wrong noiser type detected"),
93            },
94        }
95    }
96
97    /// generate and add noise to an ordinal QI type
98    fn generate_noise_nominal(
99        &mut self,
100        nominal: NominalType,
101        stream_weight: usize,
102        index: usize,
103    ) -> QuasiIdentifierTypes {
104        match self.qi_noisers.get_mut(index) {
105            None => {
106                let mut noiser = CategoricalNoiser::initialize(self.noise_thr, stream_weight);
107                let noise = noiser.generate_noise(CategoricalTypes::Nominal(nominal));
108                self.qi_noisers
109                    .push(NoiserCategories::CategoricalNoiser(noiser));
110                QuasiIdentifierTypes::Nominal(self.add_noise_nominal(noise, nominal))
111            }
112            Some(categorical) => match categorical {
113                NoiserCategories::CategoricalNoiser(noiser) => {
114                    let noise = noiser.generate_noise(CategoricalTypes::Nominal(nominal));
115                    QuasiIdentifierTypes::Nominal(self.add_noise_nominal(noise, nominal))
116                }
117                _ => panic!("wrong noiser type detected"),
118            },
119        }
120    }
121
122    /// add noise to a interval QI type value
123    pub fn add_noise_interval(&self, noise: f64, interval: IntervalType) -> IntervalType {
124        match interval {
125            (
126                QuasiIdentifierType::Float(value),
127                QuasiIdentifierType::Float(min_value),
128                QuasiIdentifierType::Float(max_value),
129                weight,
130            ) => (
131                QuasiIdentifierType::Float(truncate_to_domain(value + noise, min_value, max_value)),
132                QuasiIdentifierType::Float(min_value),
133                QuasiIdentifierType::Float(max_value),
134                weight,
135            ),
136            (
137                QuasiIdentifierType::Integer(value),
138                QuasiIdentifierType::Integer(min_value),
139                QuasiIdentifierType::Integer(max_value),
140                weight,
141            ) => (
142                QuasiIdentifierType::Integer(truncate_to_domain(
143                    (value as f64 + noise) as i32,
144                    min_value,
145                    max_value,
146                )),
147                QuasiIdentifierType::Integer(min_value),
148                QuasiIdentifierType::Integer(max_value),
149                weight,
150            ),
151            _ => {
152                panic!("Wrong typing combination when adding noise to interval value")
153            }
154        }
155    }
156
157    /// add generated noise to a nominal QI value
158    fn add_noise_nominal(&self, noise: i32, nominal: NominalType) -> NominalType {
159        let (_, max_value, weight) = nominal;
160        (noise, max_value, weight)
161    }
162
163    /// add generated noise to a ordinal QI value
164    fn add_noise_ordinal(&self, noise: i32, ordinal: OrdinalType) -> OrdinalType {
165        let (_, max_rank, weight) = ordinal;
166        (noise, max_rank, weight)
167    }
168
169    /// calculate the full weight of all the QI's
170    fn calculate_stream_weight(&self, qi: &[QuasiIdentifierTypes]) -> usize {
171        qi.iter()
172            .map(|x| match x {
173                QuasiIdentifierTypes::Interval((_, _, _, weight)) => weight,
174                QuasiIdentifierTypes::Ordinal((_, _, weight)) => weight,
175                QuasiIdentifierTypes::Nominal((_, _, weight)) => weight,
176            })
177            .sum()
178    }
179}
180
181impl Noiser for LaplaceNoiser {
182    fn add_noise<M: Anonymizable>(&mut self, value: &M) -> Vec<QuasiIdentifierTypes> {
183        let qi = value.quasi_identifiers();
184        let qi_len = qi.len();
185        let stream_weight = match self.qi_noisers.is_empty() {
186            true => self.calculate_stream_weight(&qi),
187            false => 0,
188        };
189
190        qi.into_iter()
191            .enumerate()
192            .map(|(index, x)| match x {
193                QuasiIdentifierTypes::Interval(interval) => {
194                    self.generate_noise_interval(interval, qi_len, index)
195                }
196                QuasiIdentifierTypes::Ordinal(ordinal) => {
197                    self.generate_noise_ordinal(ordinal, stream_weight, index)
198                }
199                QuasiIdentifierTypes::Nominal(nominal) => {
200                    self.generate_noise_nominal(nominal, stream_weight, index)
201                }
202            })
203            .collect::<Vec<QuasiIdentifierTypes>>()
204    }
205}