Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
@copy_docstring(Laplace.check_inputs)
def check_inputs(self, value):
super().check_inputs(value)
if not isinstance(value, Real):
raise TypeError("Value to be randomised must be a number")
if self._sensitivity is None:
raise ValueError("Sensitivity must be set")
return True
@copy_docstring(Laplace.get_bias)
def get_bias(self, value):
return 0.0
@copy_docstring(Laplace.set_sensitivity)
def set_sensitivity(self, sensitivity):
if not isinstance(sensitivity, Real):
raise TypeError("Sensitivity must be numeric")
if sensitivity <= 0:
raise ValueError("Sensitivity must be strictly positive")
self._scale = None
self._sensitivity = sensitivity
return self
@copy_docstring(Laplace.get_variance)
def get_variance(self, value):
self.check_inputs(value)
shape = self._sensitivity / self._epsilon
variance = value ** 2 + shape * (self._lower_bound * np.exp((self._lower_bound - value) / shape)
- self._upper_bound * np.exp((value - self._upper_bound) / shape))
variance += (shape ** 2) * (2 - np.exp((self._lower_bound - value) / shape)
- np.exp((value - self._upper_bound) / shape))
variance -= (self.get_bias(value) + value) ** 2
return variance
Returns
-------
float
The randomised value.
"""
self.check_inputs(value)
scale = self._sensitivity / (self._epsilon - np.log(1 - self._delta))
unif_rv = random() - 0.5
return value - scale * np.sign(unif_rv) * np.log(1 - 2 * np.abs(unif_rv))
class LaplaceTruncated(Laplace, TruncationAndFoldingMixin):
"""
The truncated Laplace mechanism, where values outside a pre-described domain are mapped to the closest point
within the domain.
"""
def __init__(self):
super().__init__()
TruncationAndFoldingMixin.__init__(self)
def __repr__(self):
output = super().__repr__()
output += TruncationAndFoldingMixin.__repr__(self)
return output
@copy_docstring(Laplace.get_bias)
def get_bias(self, value):
@copy_docstring(Laplace.check_inputs)
def check_inputs(self, value):
super().check_inputs(value)
if self._gamma is None:
self._gamma = 1 / (1 + np.exp(self._epsilon / 2))
warnings.warn("Gamma not set, falling back to default: 1 / (1 + exp(epsilon / 2)).", UserWarning)
return True
@copy_docstring(Laplace.randomise)
def randomise(self, value):
TruncationAndFoldingMixin.check_inputs(self, value)
noisy_value = super().randomise(value)
return self._fold(noisy_value)
@copy_docstring(Laplace.get_bias)
def get_bias(self, value):
self.check_inputs(value)
if self._scale is None:
self._scale = self._find_scale()
bias = (self._scale - self._lower_bound + value) / 2 * np.exp((self._lower_bound - value) / self._scale) \
- (self._scale + self._upper_bound - value) / 2 * np.exp((value - self._upper_bound) / self._scale)
bias /= 1 - np.exp((self._lower_bound - value) / self._scale) / 2 \
- np.exp((value - self._upper_bound) / self._scale) / 2
return bias
self._scale = self._find_scale()
value = min(value, self._upper_bound)
value = max(value, self._lower_bound)
unif_rv = random()
unif_rv *= self._cdf(self._upper_bound - value) - self._cdf(self._lower_bound - value)
unif_rv += self._cdf(self._lower_bound - value)
unif_rv -= 0.5
unif_rv = min(unif_rv, 0.5 - 1e-10)
return value - self._scale * np.sign(unif_rv) * np.log(1 - 2 * np.abs(unif_rv))
class LaplaceBoundedNoise(Laplace):
"""
The Laplace mechanism with bounded noise, only applicable for approximate differential privacy (delta > 0).
"""
def __init__(self):
super().__init__()
self._scale = None
self._noise_bound = None
def set_epsilon_delta(self, epsilon, delta):
r"""Set the privacy parameters :math:`\epsilon` and :math:`\delta` for the mechanism.
Epsilon must be strictly positive, `epsilon` > 0. `delta` must be strictly in the interval (0, 0.5).
- For zero `epsilon`, use :class:`.Uniform`.
- For zero `delta`, use :class:`.Laplace`.
Parameters
@copy_docstring(Laplace.randomise)
def randomise(self, value):
self.check_inputs(value)
sign = -1 if random() < 0.5 else 1
geometric_rv = geometric(1 - np.exp(- self._epsilon)) - 1
unif_rv = random()
binary_rv = 0 if random() < self._gamma / (self._gamma + (1 - self._gamma) * np.exp(- self._epsilon)) else 1
return value + sign * ((1 - binary_rv) * ((geometric_rv + self._gamma * unif_rv) * self._sensitivity) +
binary_rv * ((geometric_rv + self._gamma + (1 - self._gamma) * unif_rv) *
self._sensitivity))