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}