blob: 63e6e90077dd0bd21e983d106535084affa3f512 [file] [log] [blame]
//
// Copyright 2019 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
//
// http://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_ALGORITHMS_NUMERICAL_MECHANISMS_TESTING_H_
#define DIFFERENTIAL_PRIVACY_ALGORITHMS_NUMERICAL_MECHANISMS_TESTING_H_
#include <memory>
#include <optional>
#include <random>
#include <cstdint>
#include "gmock/gmock.h"
#include "absl/memory/memory.h"
#include "absl/random/random.h"
#include "absl/status/statusor.h"
#include "algorithms/distributions.h"
#include "algorithms/numerical-mechanisms.h"
#include "proto/confidence-interval.pb.h"
namespace differential_privacy {
namespace test_utils {
// A numerical mechanism that adds no noise to its input and does not perform
// snapping. Returns whatever is passed to it unmodified. Use only for testing.
// Not differentially private.
class ZeroNoiseMechanism : public LaplaceMechanism {
public:
class Builder : public LaplaceMechanism::Builder {
public:
Builder() : LaplaceMechanism::Builder() {}
absl::StatusOr<std::unique_ptr<NumericalMechanism>> Build() override {
return absl::StatusOr<std::unique_ptr<LaplaceMechanism>>(
absl::make_unique<ZeroNoiseMechanism>(
GetEpsilon().value_or(1), GetL1Sensitivity().value_or(1)));
}
std::unique_ptr<NumericalMechanismBuilder> Clone() const override {
return absl::make_unique<Builder>(*this);
}
};
ZeroNoiseMechanism(double epsilon, double sensitivity)
: LaplaceMechanism(epsilon, sensitivity) {}
double AddDoubleNoise(double result) override { return result; }
int64_t AddInt64Noise(int64_t result) override { return result; }
absl::StatusOr<ConfidenceInterval> NoiseConfidenceInterval(
double confidence_level) override {
ConfidenceInterval confidence;
confidence.set_lower_bound(0);
confidence.set_upper_bound(0);
confidence.set_confidence_level(confidence_level);
return confidence;
}
int64_t MemoryUsed() override { return sizeof(ZeroNoiseMechanism); }
};
class SeededGeometricDistribution : public internal::GeometricDistribution {
public:
SeededGeometricDistribution(double lambda, std::mt19937* rand_gen)
: internal::GeometricDistribution(lambda), rand_gen_(rand_gen) {}
double GetUniformDouble() override {
return absl::Uniform(*rand_gen_, 0, 1.0);
}
private:
std::mt19937* rand_gen_;
};
// A numerical distribution that generates consistent noise from a pre-seeded
// RNG, intended to make statistical tests completely reliable. Does not perform
// snapping. Use only for testing. Not differentially private.
class SeededLaplaceDistribution : public internal::LaplaceDistribution {
public:
explicit SeededLaplaceDistribution(double epsilon, double sensitivity,
std::mt19937* rand_gen = nullptr)
: internal::LaplaceDistribution(epsilon, sensitivity) {
if (rand_gen) {
rand_gen_ = rand_gen;
} else {
std::seed_seq seed({GetNumInstances()});
owned_rand_gen_ = std::mt19937(seed);
rand_gen_ = &owned_rand_gen_;
}
geometric_distro_ = absl::make_unique<SeededGeometricDistribution>(
geometric_distro_->Lambda(), rand_gen_);
}
double GetUniformDouble() override {
return absl::Uniform(*rand_gen_, 0, 1.0);
}
bool GetBoolean() override { return absl::Bernoulli(*rand_gen_, 0.5); }
protected:
std::mt19937* rand_gen_;
std::mt19937 owned_rand_gen_;
private:
// Used to ensure that different SeededLaplaceDistribution objects have
// different seeds. This is *not* thread safe.
static int GetNumInstances() {
static int num_calls = 0;
num_calls++;
return num_calls;
}
};
// A numerical mechanism using a distribution that generates consistent noise
// from a pre-seeded RNG, intended to make statistical tests completely
// reliable. Does not perform snapping. Use only for testing. Not differentially
// private.
class SeededLaplaceMechanism : public LaplaceMechanism {
public:
// Builder for SeededLaplaceMechanism.
class Builder : public LaplaceMechanism::Builder {
public:
Builder() : LaplaceMechanism::Builder() {}
absl::StatusOr<std::unique_ptr<NumericalMechanism>> Build() override {
double sensitivity;
if (GetL1Sensitivity().has_value()) {
sensitivity = *GetL1Sensitivity();
} else {
sensitivity =
GetL0Sensitivity().value_or(1) * GetLInfSensitivity().value_or(1);
}
return absl::StatusOr<std::unique_ptr<LaplaceMechanism>>(
absl::make_unique<SeededLaplaceMechanism>(GetEpsilon().value_or(1),
sensitivity, rand_gen_));
}
std::unique_ptr<NumericalMechanismBuilder> Clone() const override {
return absl::make_unique<Builder>(*this);
}
SeededLaplaceMechanism::Builder& rand_gen(std::mt19937* rand_gen) {
rand_gen_ = rand_gen;
return *this;
}
private:
std::mt19937* rand_gen_ = nullptr;
};
explicit SeededLaplaceMechanism(double epsilon, double sensitivity = 1.0)
: LaplaceMechanism(epsilon, sensitivity,
absl::make_unique<SeededLaplaceDistribution>(
epsilon, sensitivity)) {}
explicit SeededLaplaceMechanism(double epsilon, double sensitivity,
std::mt19937* rand_gen)
: LaplaceMechanism(epsilon, sensitivity,
absl::make_unique<SeededLaplaceDistribution>(
epsilon, sensitivity, rand_gen)) {}
};
// A mock Laplace mechanism using gmock. Can be set to return any value.
class MockLaplaceMechanism : public LaplaceMechanism {
public:
// Builder for MockLaplaceMechanism.
class Builder : public LaplaceMechanism::Builder {
public:
Builder()
: LaplaceMechanism::Builder(),
mock_(absl::make_unique<MockLaplaceMechanism>()) {}
// Can only be called once.
absl::StatusOr<std::unique_ptr<NumericalMechanism>> Build() override {
return absl::StatusOr<std::unique_ptr<LaplaceMechanism>>(
std::unique_ptr<LaplaceMechanism>(mock_.release()));
}
MockLaplaceMechanism* mock() { return mock_.get(); }
std::unique_ptr<NumericalMechanismBuilder> Clone() const override {
return absl::make_unique<MockLaplaceMechanism::Builder>();
}
private:
std::unique_ptr<MockLaplaceMechanism> mock_;
};
MockLaplaceMechanism() : LaplaceMechanism(1, 1) {
}
MockLaplaceMechanism(double epsilon, double sensitivity)
: LaplaceMechanism(epsilon, sensitivity) {
}
MOCK_METHOD(double, AddDoubleNoise, (double result), (override));
MOCK_METHOD(int64_t, AddInt64Noise, (int64_t result), (override));
MOCK_METHOD(absl::StatusOr<ConfidenceInterval>, NoiseConfidenceInterval,
(double confidence_level), (override));
MOCK_METHOD(int64_t, MemoryUsed, (), (override));
};
} // namespace test_utils
} // namespace differential_privacy
#endif // DIFFERENTIAL_PRIVACY_ALGORITHMS_NUMERICAL_MECHANISMS_TESTING_H_