concrete_core/specification/
dispersion.rs

1//! Noise distribution
2//!
3//! When dealing with noise, we tend to use different representation for the same value. In
4//! general, the noise is specified by the standard deviation of a gaussian distribution, which
5//! is of the form $\sigma = 2^p$, with $p$ a negative integer. Depending on the use case though,
6//! we rely on different representations for this quantity:
7//!
8//! + $\sigma$ can be encoded in the [`StandardDev`] type.
9//! + $p$ can be encoded in the [`LogStandardDev`] type.
10//! + $\sigma^2$ can be encoded in the [`Variance`] type.
11//!
12//! In any of those cases, the corresponding type implements the `DispersionParameter` trait,
13//! which makes if possible to use any of those representations generically when noise must be
14//! defined.
15
16#[cfg(feature = "__commons_serialization")]
17use serde::{Deserialize, Serialize};
18
19/// A trait for types representing distribution parameters, for a given unsigned integer type.
20//  Warning:
21//  DispersionParameter type should ONLY wrap a single native type.
22//  As long as Variance wraps a native type (f64) it is ok to derive it from Copy instead of
23//  Clone because f64 is itself Copy and stored in register.
24pub trait DispersionParameter: Copy {
25    /// Returns the standard deviation of the distribution, i.e. $\sigma = 2^p$.
26    fn get_standard_dev(&self) -> f64;
27    /// Returns the variance of the distribution, i.e. $\sigma^2 = 2^{2p}$.
28    fn get_variance(&self) -> f64;
29    /// Returns base 2 logarithm of the standard deviation of the distribution, i.e.
30    /// $\log\_2(\sigma)=p$
31    fn get_log_standard_dev(&self) -> f64;
32    /// For a `Uint` type representing $\mathbb{Z}/2^q\mathbb{Z}$, we return $2^{q-p}$.
33    fn get_modular_standard_dev(&self, log2_modulus: u32) -> f64;
34
35    /// For a `Uint` type representing $\mathbb{Z}/2^q\mathbb{Z}$, we return $2^{2(q-p)}$.
36    fn get_modular_variance(&self, log2_modulus: u32) -> f64;
37
38    /// For a `Uint` type representing $\mathbb{Z}/2^q\mathbb{Z}$, we return $q-p$.
39    fn get_modular_log_standard_dev(&self, log2_modulus: u32) -> f64;
40}
41
42/// A distribution parameter that uses the base-2 logarithm of the standard deviation as
43/// representation.
44///
45/// # Example:
46///
47/// ```
48/// use concrete_core::prelude::{DispersionParameter, LogStandardDev};
49/// let params = LogStandardDev::from_log_standard_dev(-25.);
50/// assert_eq!(params.get_standard_dev(), 2_f64.powf(-25.));
51/// assert_eq!(params.get_log_standard_dev(), -25.);
52/// assert_eq!(params.get_variance(), 2_f64.powf(-25.).powi(2));
53/// assert_eq!(params.get_modular_standard_dev(32), 2_f64.powf(32. - 25.),);
54/// assert_eq!(params.get_modular_log_standard_dev(32), 32. - 25.);
55/// assert_eq!(
56///     params.get_modular_variance(32),
57///     2_f64.powf(32. - 25.).powi(2)
58/// );
59///
60/// let modular_params = LogStandardDev::from_modular_log_standard_dev(22., 32);
61/// assert_eq!(modular_params.get_standard_dev(), 2_f64.powf(-10.));
62/// ```
63#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
64pub struct LogStandardDev(pub f64);
65
66impl LogStandardDev {
67    pub fn from_log_standard_dev(log_std: f64) -> LogStandardDev {
68        LogStandardDev(log_std)
69    }
70
71    pub fn from_modular_log_standard_dev(log_std: f64, log2_modulus: u32) -> LogStandardDev {
72        LogStandardDev(log_std - log2_modulus as f64)
73    }
74}
75
76impl DispersionParameter for LogStandardDev {
77    fn get_standard_dev(&self) -> f64 {
78        f64::powf(2., self.0)
79    }
80    fn get_variance(&self) -> f64 {
81        f64::powf(2., self.0 * 2.)
82    }
83    fn get_log_standard_dev(&self) -> f64 {
84        self.0
85    }
86    fn get_modular_standard_dev(&self, log2_modulus: u32) -> f64 {
87        f64::powf(2., log2_modulus as f64 + self.0)
88    }
89    fn get_modular_variance(&self, log2_modulus: u32) -> f64 {
90        f64::powf(2., (log2_modulus as f64 + self.0) * 2.)
91    }
92    fn get_modular_log_standard_dev(&self, log2_modulus: u32) -> f64 {
93        log2_modulus as f64 + self.0
94    }
95}
96
97/// A distribution parameter that uses the standard deviation as representation.
98///
99/// # Example:
100///
101/// ```
102/// use concrete_core::prelude::{DispersionParameter, StandardDev};
103/// let params = StandardDev::from_standard_dev(2_f64.powf(-25.));
104/// assert_eq!(params.get_standard_dev(), 2_f64.powf(-25.));
105/// assert_eq!(params.get_log_standard_dev(), -25.);
106/// assert_eq!(params.get_variance(), 2_f64.powf(-25.).powi(2));
107/// assert_eq!(params.get_modular_standard_dev(32), 2_f64.powf(32. - 25.));
108/// assert_eq!(params.get_modular_log_standard_dev(32), 32. - 25.);
109/// assert_eq!(
110///     params.get_modular_variance(32),
111///     2_f64.powf(32. - 25.).powi(2)
112/// );
113/// ```
114#[cfg_attr(feature = "__commons_serialization", derive(Serialize, Deserialize))]
115#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
116pub struct StandardDev(pub f64);
117
118impl StandardDev {
119    pub fn from_standard_dev(std: f64) -> StandardDev {
120        StandardDev(std)
121    }
122
123    pub fn from_modular_standard_dev(std: f64, log2_modulus: u32) -> StandardDev {
124        StandardDev(std / 2_f64.powf(log2_modulus as f64))
125    }
126}
127
128impl DispersionParameter for StandardDev {
129    fn get_standard_dev(&self) -> f64 {
130        self.0
131    }
132    fn get_variance(&self) -> f64 {
133        self.0.powi(2)
134    }
135    fn get_log_standard_dev(&self) -> f64 {
136        self.0.log2()
137    }
138    fn get_modular_standard_dev(&self, log2_modulus: u32) -> f64 {
139        2_f64.powf(log2_modulus as f64 + self.0.log2())
140    }
141    fn get_modular_variance(&self, log2_modulus: u32) -> f64 {
142        2_f64.powf(2. * (log2_modulus as f64 + self.0.log2()))
143    }
144    fn get_modular_log_standard_dev(&self, log2_modulus: u32) -> f64 {
145        log2_modulus as f64 + self.0.log2()
146    }
147}
148
149/// A distribution parameter that uses the variance as representation
150///
151/// # Example:
152///
153/// ```
154/// use concrete_core::prelude::{DispersionParameter, Variance};
155/// let params = Variance::from_variance(2_f64.powi(-50));
156/// assert_eq!(params.get_standard_dev(), 2_f64.powf(-25.));
157/// assert_eq!(params.get_log_standard_dev(), -25.);
158/// assert_eq!(params.get_variance(), 2_f64.powf(-25.).powi(2));
159/// assert_eq!(params.get_modular_standard_dev(32), 2_f64.powf(32. - 25.));
160/// assert_eq!(params.get_modular_log_standard_dev(32), 32. - 25.);
161/// assert_eq!(
162///     params.get_modular_variance(32),
163///     2_f64.powf(32. - 25.).powi(2)
164/// );
165/// ```
166#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
167pub struct Variance(pub f64);
168
169impl Variance {
170    pub fn from_variance(var: f64) -> Variance {
171        Variance(var)
172    }
173
174    pub fn from_modular_variance(var: f64, log2_modulus: u32) -> Variance {
175        Variance(var / 2_f64.powf(log2_modulus as f64 * 2.))
176    }
177}
178
179impl DispersionParameter for Variance {
180    fn get_standard_dev(&self) -> f64 {
181        self.0.sqrt()
182    }
183    fn get_variance(&self) -> f64 {
184        self.0
185    }
186    fn get_log_standard_dev(&self) -> f64 {
187        self.0.sqrt().log2()
188    }
189    fn get_modular_standard_dev(&self, log2_modulus: u32) -> f64 {
190        2_f64.powf(log2_modulus as f64 + self.0.sqrt().log2())
191    }
192    fn get_modular_variance(&self, log2_modulus: u32) -> f64 {
193        2_f64.powf(2. * (log2_modulus as f64 + self.0.sqrt().log2()))
194    }
195    fn get_modular_log_standard_dev(&self, log2_modulus: u32) -> f64 {
196        log2_modulus as f64 + self.0.sqrt().log2()
197    }
198}