| // Copyright 2020 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef DIFFERENTIAL_PRIVACY_ACCOUNTING_PRIVACY_LOSS_MECHANISM_H_ |
| #define DIFFERENTIAL_PRIVACY_ACCOUNTING_PRIVACY_LOSS_MECHANISM_H_ |
| |
| #include "base/logging.h" |
| #include "absl/status/status.h" |
| #include "base/statusor.h" |
| #include "absl/types/optional.h" |
| #include "boost/math/distributions/laplace.hpp" |
| #include "boost/math/distributions/normal.hpp" |
| #include "accounting/common/common.h" |
| // Implementing privacy loss for additive noise mechanisms. |
| // Please refer to the supplementary material below for more details: |
| // ../../common_docs/Privacy_Loss_Distributions.pdf |
| |
| namespace differential_privacy { |
| namespace accounting { |
| |
| // Representation of the tail of privacy loss distribution. |
| struct PrivacyLossTail { |
| // The minimum value of x that should be considered after the tail |
| // is discarded. |
| double lower_x_truncation = 0; |
| // The maximum value of x that should be considered after the tail |
| // is discarded. |
| double upper_x_truncation = 0; |
| // The probability mass of the privacy loss distribution that has to be added |
| // due to the discarded tail. Each key is a privacy loss value and the |
| // corresponding value is the probability mass that the value occurs. |
| ProbabilityMassFunctionOf<double> probability_mass_function = {}; |
| }; |
| |
| // Privacy loss of additive noise mechanisms. |
| // |
| // An additive noise mechanism for computing a scalar-valued function f is a |
| // mechanism that outputs the sum of the true value of the function and a noise |
| // drawn from a certain distribution mu. |
| // |
| // We assume that the noise mu is such that the algorithm is more private as |
| // the sensitivity of f decreases. (Recall that the sensitivity of f is the |
| // maximum absolute change in f when an input to a single user changes.) |
| // Under this assumption, the PLD of the mechanism is exactly generated as |
| // follows: pick x from mu and let the privacy loss be |
| // ln(P(x) / P(x - sensitivity)). Note that when mu is discrete, |
| // P(x) and P(x - sensitivity) are the probability masses of mu at x and |
| // x - sensitivity respectively. When mu is continuous, P(x) and |
| // P(x - sensitivity) are the probability densities of mu at x and |
| // x - sensitivity respectively. |
| // |
| // This class also assumes the privacy loss is non-increasing as x increases. |
| class AdditiveNoisePrivacyLoss { |
| public: |
| virtual ~AdditiveNoisePrivacyLoss() {} |
| |
| // Whether the noise is discrete. |
| virtual NoiseType Discrete() const = 0; |
| |
| // Computes the tail of the privacy loss distribution. |
| virtual PrivacyLossTail PrivacyLossDistributionTail() const = 0; |
| |
| // Computes the privacy loss at a given point. |
| virtual double PrivacyLoss(double x) const = 0; |
| |
| // Returns the largest x such that the privacy loss at x is at least |
| // privacy_loss. |
| // We assume that privacy loss is non-increasing as x increases |
| // This is true for Laplace, Gaussian and other widely used mechanisms such |
| // as DiscreteLaplace. |
| virtual double InversePrivacyLoss(double privacy_loss) const = 0; |
| |
| // Cumulative density function of the noise distribution. |
| // Returns cumulative density function of noise at x, i.e. the probability |
| // that mu is less than or equal to x. |
| virtual double NoiseCdf(double x) const = 0; |
| |
| // Computes the epsilon-hockey stick divergence of the mechanism. |
| // That is for a given epsilon returns delta such that the mechanism is |
| // (epsilon, delta)-DP. |
| virtual double GetDeltaForEpsilon(double epsilon) const { |
| const double x_cutoff = InversePrivacyLoss(epsilon); |
| return NoiseCdf(x_cutoff) - |
| std::exp(epsilon) * NoiseCdf(x_cutoff - sensitivity_); |
| } |
| |
| double Sensitivity() const { return sensitivity_; } |
| |
| protected: |
| explicit AdditiveNoisePrivacyLoss(double sensitivity = 1) |
| : sensitivity_(sensitivity) {} |
| |
| const double sensitivity_; |
| }; |
| |
| // Privacy loss of the Laplace mechanism. |
| // |
| // The Laplace mechanism for computing a scalar-valued function f simply outputs |
| // the sum of the true value of the function and a noise drawn from the Laplace |
| // distribution. Recall that the Laplace distribution with parameter b has |
| // probability density function 0.5/b * exp(-|x|/b) at x for any real number x. |
| // |
| // The privacy loss distribution of the Laplace mechanism is equivalent to the |
| // privacy loss distribution between the Laplace distribution and the same |
| // distribution but shifted by the sensitivity of f. Specifically, the privacy |
| // loss distribution of the Laplace mechanism is generated as follows: first |
| // pick x according to the Laplace noise. Then, let the privacy loss be |
| // ln(PDF(x) / PDF(x - sensitivity)) which is equal to |
| // (|x - sensitivity| - |x|) / parameter. |
| class LaplacePrivacyLoss : public AdditiveNoisePrivacyLoss { |
| public: |
| // Creates LaplacePrivacyLoss from these parameters: |
| // parameter: the parameter of the Laplace distribution. |
| // sensitivity: the sensitivity of function f. (i.e. the maximum absolute |
| // change in f when an input to a single user changes.) |
| static base::StatusOr<std::unique_ptr<LaplacePrivacyLoss>> Create( |
| double parameter, double sensitivity); |
| |
| // Creates LaplacePrivacyLoss from epsilon and delta. |
| static base::StatusOr<std::unique_ptr<LaplacePrivacyLoss>> Create( |
| const EpsilonDelta& epsilon_delta); |
| |
| NoiseType Discrete() const override { return NoiseType::kContinuous; } |
| |
| double InversePrivacyLoss(double privacy_loss) const override; |
| |
| double NoiseCdf(double x) const override; |
| |
| double PrivacyLoss(double x) const override; |
| |
| // Computes the privacy loss at the tail of Laplace distribution. |
| PrivacyLossTail PrivacyLossDistributionTail() const override; |
| |
| double Parameter() const { return parameter_; } |
| |
| private: |
| LaplacePrivacyLoss(double parameter, double sensitivity) |
| : AdditiveNoisePrivacyLoss(sensitivity), parameter_(parameter) { |
| distribution_ = boost::math::laplace_distribution<double>(0, parameter); |
| } |
| |
| boost::math::laplace_distribution<double> distribution_; |
| const double parameter_; |
| }; |
| |
| // Privacy loss of the Gaussian mechanism. |
| // |
| // The Gaussian mechanism for computing a scalar-valued function f simply |
| // outputs the sum of the true value of the function and a noise drawn from the |
| // Gaussian distribution. Recall that the (centered) Gaussian distribution with |
| // standard deviation sigma has probability density function |
| // 1/(sigma * sqrt(2 * pi)) * exp(-0.5 x^2/sigma^2) at x for any real number x. |
| |
| // The privacy loss distribution of the Gaussian mechanism is |
| // equivalent to the privacy loss distribution between the Gaussian distribution |
| // and the same distribution but shifted by the sensitivity of f. Specifically, |
| // the privacy loss distribution of the Gaussian mechanism is generated as |
| // follows: first pick x according to the Gaussian noise. Then, let the privacy |
| // loss be ln(PDF(x) / PDF(x - sensitivity)) which is equal to |
| // 0.5 * sensitivity * (sensitivity - 2 * x) / sigma^2. |
| class GaussianPrivacyLoss : public AdditiveNoisePrivacyLoss { |
| public: |
| // Creates GaussianPrivacyLoss from these parameters: |
| // standard_deviation: standard deviation of Gaussian noise |
| // sensitivity: the sensitivity of function f. (i.e. the maximum absolute |
| // change in f when an input to a single user changes.) |
| // estimate_type: kPessimistic denoting that the rounding is done in |
| // such a way that the resulting epsilon-hockey stick divergence |
| // computation gives an upper estimate to the real value. |
| // mass_truncation_bound: the ln of the probability mass that might be |
| // discarded from the noise distribution. The larger this number, |
| // the more error it may introduce in divergence calculations. |
| static base::StatusOr<std::unique_ptr<GaussianPrivacyLoss>> Create( |
| double standard_deviation, double sensitivity, |
| EstimateType estimate_type = EstimateType::kPessimistic, |
| double mass_truncation_bound = -50); |
| |
| NoiseType Discrete() const override { return NoiseType::kContinuous; } |
| |
| static base::StatusOr<std::unique_ptr<GaussianPrivacyLoss>> Create( |
| const EpsilonDelta& epsilon_delta, |
| EstimateType estimate_type = EstimateType::kPessimistic, |
| double mass_truncation_bound = -50); |
| |
| // Composes with itself num_times. |
| // The composition with itself num_times is the same as the |
| // GaussianPrivacyLoss with sensitivity scaled up by a factor of square root |
| // of num_times. |
| base::StatusOr<std::unique_ptr<GaussianPrivacyLoss>> Compose(int num_times); |
| |
| double InversePrivacyLoss(double privacy_loss) const override; |
| |
| double NoiseCdf(double x) const override; |
| |
| double PrivacyLoss(double x) const override; |
| |
| PrivacyLossTail PrivacyLossDistributionTail() const override; |
| |
| double StandardDeviation() const { return standard_deviation_; } |
| |
| private: |
| GaussianPrivacyLoss(double standard_deviation, double sensitivity, |
| EstimateType estimate_type, double mass_truncation_bound) |
| : AdditiveNoisePrivacyLoss(sensitivity), |
| standard_deviation_(standard_deviation), |
| estimate_type_(estimate_type), |
| mass_truncation_bound_(mass_truncation_bound) { |
| distribution_ = |
| boost::math::normal_distribution<double>(0, standard_deviation); |
| } |
| const double standard_deviation_; |
| const EstimateType estimate_type_; |
| const double mass_truncation_bound_; |
| boost::math::normal_distribution<double> distribution_; |
| }; |
| |
| // Privacy loss of the discrete Laplace mechanism. |
| // |
| // The discrete Laplace mechanism for computing an integer-valued function f |
| // simply outputs the sum of the true value of the function and a noise drawn |
| // from the discrete Laplace distribution. Recall that the discrete Laplace |
| // distribution with parameter a > 0 has probability mass function |
| // Z * exp(-a * |x|) at x for any integer x, where Z = (e^a - 1) / (e^a + 1). |
| // |
| // The privacy loss distribution of the discrete Laplace mechanism is equivalent |
| // to that between the discrete Laplace distribution and the same distribution |
| // but shifted by the sensitivity. More specifically, the privacy loss |
| // distribution of the discrete Laplace mechanism is generated as follows: first |
| // pick x according to the discrete Laplace noise. Then, let the privacy loss be |
| // ln(PMF(x) / PMF(x - sensitivity)) which is equal to |
| // parameter * (|x - sensitivity| - |x|). |
| class DiscreteLaplacePrivacyLoss : public AdditiveNoisePrivacyLoss { |
| public: |
| // Creates DiscreteLaplacePrivacyLoss from these parameters: |
| // parameter: the parameter of the discrete Laplace distribution. |
| // sensitivity: the sensitivity of function f. (i.e. the maximum absolute |
| // change in f when an input to a single user changes.) |
| static base::StatusOr<std::unique_ptr<DiscreteLaplacePrivacyLoss>> Create( |
| double parameter, double sensitivity); |
| |
| // Creates DiscreteLaplacePrivacyLoss from epsilon, delta and sensitivity. |
| static base::StatusOr<std::unique_ptr<DiscreteLaplacePrivacyLoss>> Create( |
| const EpsilonDelta& epsilon_delta, const double sensitivity); |
| |
| NoiseType Discrete() const override { return NoiseType::kDiscrete; } |
| |
| double InversePrivacyLoss(double privacy_loss) const override; |
| |
| double NoiseCdf(double x) const override; |
| |
| double PrivacyLoss(double x) const override; |
| |
| // Computes the privacy loss at the tail of discrete Laplace distribution. |
| PrivacyLossTail PrivacyLossDistributionTail() const override; |
| |
| double Parameter() const { return parameter_; } |
| |
| private: |
| DiscreteLaplacePrivacyLoss(double parameter, double sensitivity) |
| : AdditiveNoisePrivacyLoss(sensitivity), parameter_(parameter) {} |
| |
| const double parameter_; |
| }; |
| |
| // Privacy loss of the discrete Gaussian mechanism. |
| // |
| // The discrete Gaussian mechanism for computing a scalar-valued function f |
| // simply outputs the sum of the true value of the function and a noise drawn |
| // from the discrete Gaussian distribution. Recall that the (centered) discrete |
| // Gaussian distribution with parameter sigma has probability mass function |
| // proportional to exp(-0.5 x^2/sigma^2) at x for any integer x. Since its |
| // normalization factor and cumulative density function do not have a closed |
| // form, we will instead consider the truncated version where the noise x is |
| // restricted to only be in [-truncated_bound, truncated_bound]. |
| |
| // The privacy loss distribution of the discrete Gaussian mechanism is |
| // equivalent to the privacy loss distribution between the discrete Gaussian |
| // distribution and the same distribution but shifted by the sensitivity of f. |
| // Specifically, the privacy loss distribution of the discrete Gaussian |
| // mechanism is generated as follows: first pick x according to the discrete |
| // Gaussian noise. Then, let the privacy loss be |
| // ln(PMF(x) / PMF(x - sensitivity)) which is equal to |
| // 0.5 * sensitivity * (sensitivity - 2 * x) / sigma^2. Note that since we |
| // consider the truncated version of the noise, we set the privacy loss to |
| // infinity when x < -truncation_bound + sensitivity. |
| // |
| // Reference: |
| // Canonne, Kamath, Steinke. "The Discrete Gaussian for Differential Privacy". |
| // In NeurIPS 2020. |
| class DiscreteGaussianPrivacyLoss : public AdditiveNoisePrivacyLoss { |
| public: |
| // Creates DiscreteGaussianPrivacyLoss from these parameters: |
| // sigma: the parameter of the discrete Gaussian distribution. Note that |
| // unlike the (continuous) Gaussian distribution this is not equal to the |
| // standard deviation of the noise. |
| // sensitivity: the sensitivity of function f. (i.e. the maximum absolute |
| // change in f when an input to a single user changes.) |
| // truncation_bound: bound for truncating the noise, i.e. the noise will only |
| // have a support in [-truncation_bound, truncation_bound]. When not set, |
| // truncation_bound will be chosen in such a way that the mass of the noise |
| // outside of this range is at most 1e-30. |
| static base::StatusOr<std::unique_ptr<DiscreteGaussianPrivacyLoss>> Create( |
| double sigma, int sensitivity, |
| absl::optional<int> truncation_bound = absl::nullopt); |
| |
| NoiseType Discrete() const override { return NoiseType::kDiscrete; } |
| |
| static base::StatusOr<std::unique_ptr<DiscreteGaussianPrivacyLoss>> Create( |
| const EpsilonDelta& epsilon_delta, int sensitivity, |
| absl::optional<int> truncation_bound); |
| |
| double InversePrivacyLoss(double privacy_loss) const override; |
| |
| double NoiseCdf(double x) const override; |
| |
| double PrivacyLoss(double x) const override; |
| |
| PrivacyLossTail PrivacyLossDistributionTail() const override; |
| |
| double Sigma() const { return sigma_; } |
| |
| double StandardDeviation() const; |
| |
| int TruncationBound() const { return truncation_bound_; } |
| |
| private: |
| DiscreteGaussianPrivacyLoss(double sigma, int sensitivity, |
| int truncation_bound, |
| ProbabilityMassFunction noise_pmf, |
| CumulativeDensityFunction noise_cdf) |
| : AdditiveNoisePrivacyLoss(sensitivity), |
| sigma_(sigma), |
| truncation_bound_(truncation_bound), |
| noise_pmf_(noise_pmf), |
| noise_cdf_(noise_cdf) {} |
| const double sigma_; |
| const int truncation_bound_; |
| const ProbabilityMassFunction noise_pmf_; |
| const CumulativeDensityFunction noise_cdf_; |
| }; |
| } // namespace accounting |
| } // namespace differential_privacy |
| |
| #endif // DIFFERENTIAL_PRIVACY_ACCOUNTING_PRIVACY_LOSS_MECHANISM_H_ |