Improvements in Accounting, refactoring in C++ & Go
Accounting:
- Support noise_multiplier=0 and sampling_probability=0 in (Poisson
subsampled) Gaussian and Laplace DpEvent
- Fix behavior of RdpAccountant when sampling_probability=0 and
noise_multiplier=0 in Poisson subsampled Gaussian DpEvent
- Add [0, 1] as default lower-endpoint-and-guess bracket interval.
Remove redundant check of bracket interval, since brentq also raises
when interval is bad
C++:
- Refactoring of partition selection
Go:
- Add go.sum
Change-Id: I1f6219a51f43218693aa9b32c51b585469a1eb28
GitOrigin-RevId: 6bf7243904c5d31ad6670832912da2c9c19ebc38
diff --git a/cc/algorithms/BUILD b/cc/algorithms/BUILD
index 79b7d8c..b34f208 100644
--- a/cc/algorithms/BUILD
+++ b/cc/algorithms/BUILD
@@ -600,6 +600,7 @@
":numerical-mechanisms",
":rand",
":util",
+ "@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_cc_differential_privacy//base:status",
],
@@ -610,8 +611,11 @@
srcs = ["partition-selection_test.cc"],
shard_count = 2,
deps = [
+ ":numerical-mechanisms",
":numerical-mechanisms-testing",
":partition-selection",
+ "//base/testing:status_matchers",
+ "@com_google_absl//absl/memory",
"@com_google_absl//absl/status:statusor",
"@com_google_googletest//:gtest_main",
],
diff --git a/cc/algorithms/partition-selection.h b/cc/algorithms/partition-selection.h
index 469dddd..e98287f 100644
--- a/cc/algorithms/partition-selection.h
+++ b/cc/algorithms/partition-selection.h
@@ -24,6 +24,7 @@
#include <string>
#include <utility>
+#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "algorithms/distributions.h"
#include "algorithms/numerical-mechanisms.h"
@@ -33,65 +34,13 @@
namespace differential_privacy {
-// Provides a common abstraction for PartitionSelectionStrategy. Each partition
-// selection strategy class has a builder with which it can be instantiated, and
-// calling ShouldKeep will return true if a partition with the given number of
-// users should be kept based on the values the partition selection strategy
-// was instantiated with (while ShouldKeep will return false if the partition
-// should have been dropped).
+// Provides a common abstraction for PartitionSelectionStrategy. Calling
+// ShouldKeep will return true if a partition with the given number of users
+// should be kept based on the values the partition selection strategy was
+// instantiated with (while ShouldKeep will return false if the partition should
+// have been dropped).
class PartitionSelectionStrategy {
public:
- // Builder base class
- class Builder {
- public:
- virtual ~Builder() = default;
-
- Builder& SetEpsilon(double epsilon) {
- epsilon_ = epsilon;
- return *this;
- }
-
- Builder& SetDelta(double delta) {
- delta_ = delta;
- return *this;
- }
-
- Builder& SetMaxPartitionsContributed(int64_t max_partitions_contributed) {
- max_partitions_contributed_ = max_partitions_contributed;
- return *this;
- }
-
- virtual absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>>
- Build() = 0;
-
- protected:
- // Convenience methods to check if the Builder variables are set & valid
- absl::Status EpsilonIsSetAndValid() {
- return PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon_);
- }
- absl::Status DeltaIsSetAndValid() {
- return PartitionSelectionStrategy::DeltaIsSetAndValid(delta_);
- }
- absl::Status MaxPartitionsContributedIsSetAndValid() {
- return PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
- max_partitions_contributed_);
- }
-
- absl::optional<double> GetEpsilon() { return epsilon_; }
-
- absl::optional<double> GetDelta() { return delta_; }
-
- absl::optional<int64_t> GetMaxPartitionsContributed() {
- return max_partitions_contributed_;
- }
-
- absl::optional<double> delta_;
-
- private:
- absl::optional<double> epsilon_;
- absl::optional<int64_t> max_partitions_contributed_;
- };
-
virtual ~PartitionSelectionStrategy() = default;
double GetEpsilon() const { return epsilon_; }
@@ -102,6 +51,9 @@
return max_partitions_contributed_;
}
+ // This is set with the results from `CalculateAdjustedDelta`.
+ double GetAdjustedDelta() const { return adjusted_delta_; }
+
// ShouldKeep returns true when a partition with a given number of users
// should be kept and false otherwise.
virtual bool ShouldKeep(double num_users) = 0;
@@ -117,29 +69,6 @@
max_partitions_contributed_(max_partitions_contributed),
adjusted_delta_(adjusted_delta) {}
- // Checks if epsilon is set and valid.
- static absl::Status EpsilonIsSetAndValid(absl::optional<double> epsilon) {
- RETURN_IF_ERROR(ValidateIsFiniteAndPositive(epsilon, "Epsilon"));
- return absl::OkStatus();
- }
-
- // Checks if delta is set and valid.
- static absl::Status DeltaIsSetAndValid(absl::optional<double> delta) {
- RETURN_IF_ERROR(ValidateIsInInclusiveInterval(delta, 0, 1, "Delta"));
- return absl::OkStatus();
- }
-
- // Checks if the max number of partitions contributed to is set and valid.
- static absl::Status MaxPartitionsContributedIsSetAndValid(
- absl::optional<int64_t> max_partitions_contributed) {
- RETURN_IF_ERROR(ValidateIsPositive(
- max_partitions_contributed,
- "Max number of partitions a user can contribute to"));
- return absl::OkStatus();
- }
-
- double GetAdjustedDelta() const { return adjusted_delta_; }
-
// We must derive an adjusted delta, to be used as the probability of keeping
// a single partition with one user, from delta, the probability we keep any
// of the partitions contributed to by a single user. Since the probability
@@ -148,10 +77,9 @@
// contribute to will get us delta, we can solve to get the following formula.
static absl::StatusOr<double> CalculateAdjustedDelta(
double delta, int64_t max_partitions_contributed) {
- RETURN_IF_ERROR(PartitionSelectionStrategy::DeltaIsSetAndValid(delta));
+ RETURN_IF_ERROR(ValidateDelta(delta));
RETURN_IF_ERROR(
- PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
- max_partitions_contributed));
+ ValidateMaxPartitionsContributed(max_partitions_contributed));
// Numerically stable equivalent of
// 1- pow(1 - delta, 1 / max_partitions_contributed).
@@ -164,11 +92,9 @@
// Inverse of CalculateAdjustedDelta()
static absl::StatusOr<double> CalculateUnadjustedDelta(
double adjusted_delta, int64_t max_partitions_contributed) {
+ RETURN_IF_ERROR(ValidateDelta(adjusted_delta));
RETURN_IF_ERROR(
- PartitionSelectionStrategy::DeltaIsSetAndValid(adjusted_delta));
- RETURN_IF_ERROR(
- PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
- max_partitions_contributed));
+ ValidateMaxPartitionsContributed(max_partitions_contributed));
// Numerically stable equivalent of
// 1 - pow(1 - adjusted_delta, max_partitions_contributed).
@@ -185,6 +111,46 @@
double adjusted_delta_;
};
+// Provides a common abstraction for PartitionSelectionStrategy builders. Each
+// partition selection strategy builder inherits from this builder.
+class PartitionSelectionStrategyBuilder {
+ public:
+ virtual ~PartitionSelectionStrategyBuilder() = default;
+
+ PartitionSelectionStrategyBuilder& SetEpsilon(double epsilon) {
+ epsilon_ = epsilon;
+ return *this;
+ }
+
+ PartitionSelectionStrategyBuilder& SetDelta(double delta) {
+ delta_ = delta;
+ return *this;
+ }
+
+ PartitionSelectionStrategyBuilder& SetMaxPartitionsContributed(
+ int64_t max_partitions_contributed) {
+ max_partitions_contributed_ = max_partitions_contributed;
+ return *this;
+ }
+
+ virtual absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>>
+ Build() = 0;
+
+ protected:
+ std::optional<double> GetEpsilon() { return epsilon_; }
+
+ std::optional<double> GetDelta() { return delta_; }
+
+ std::optional<int64_t> GetMaxPartitionsContributed() {
+ return max_partitions_contributed_;
+ }
+
+ private:
+ std::optional<double> epsilon_;
+ std::optional<double> delta_;
+ std::optional<int64_t> max_partitions_contributed_;
+};
+
// NearTruncatedGeometricPartitionSelection implements magic partition selection
// - instead of calculating a specific threshold to determine whether or not a
// partition should be kept, magic partition selection uses a formula derived
@@ -198,13 +164,14 @@
: public PartitionSelectionStrategy {
public:
// Builder for NearTruncatedGeometricPartitionSelection
- class Builder : public PartitionSelectionStrategy::Builder {
+ class Builder : public PartitionSelectionStrategyBuilder {
public:
absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>> Build()
override {
- RETURN_IF_ERROR(EpsilonIsSetAndValid());
- RETURN_IF_ERROR(DeltaIsSetAndValid());
- RETURN_IF_ERROR(MaxPartitionsContributedIsSetAndValid());
+ RETURN_IF_ERROR(ValidateEpsilon(GetEpsilon()));
+ RETURN_IF_ERROR(ValidateDelta(GetDelta()));
+ RETURN_IF_ERROR(
+ ValidateMaxPartitionsContributed(GetMaxPartitionsContributed()));
ASSIGN_OR_RETURN(
double adjusted_delta,
@@ -278,7 +245,6 @@
double adjusted_epsilon_;
double crossover_1_;
double crossover_2_;
-
};
// PreaggPartitionSelection is the deprecated name for
@@ -292,7 +258,7 @@
class LaplacePartitionSelection : public PartitionSelectionStrategy {
public:
// Builder for LaplacePartitionSelection
- class Builder : public PartitionSelectionStrategy::Builder {
+ class Builder : public PartitionSelectionStrategyBuilder {
public:
Builder& SetLaplaceMechanism(
std::unique_ptr<LaplaceMechanism::Builder> laplace_builder) {
@@ -302,9 +268,11 @@
absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>> Build()
override {
- RETURN_IF_ERROR(EpsilonIsSetAndValid());
- RETURN_IF_ERROR(DeltaIsSetAndValid());
- RETURN_IF_ERROR(MaxPartitionsContributedIsSetAndValid());
+ RETURN_IF_ERROR(ValidateEpsilon(GetEpsilon()));
+ RETURN_IF_ERROR(ValidateDelta(GetDelta()));
+ RETURN_IF_ERROR(
+ ValidateMaxPartitionsContributed(GetMaxPartitionsContributed()));
+
if (laplace_builder_ == nullptr) {
laplace_builder_ = absl::make_unique<LaplaceMechanism::Builder>();
}
@@ -357,10 +325,9 @@
static absl::StatusOr<double> CalculateDelta(
double epsilon, double threshold, int64_t max_partitions_contributed) {
- RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
+ RETURN_IF_ERROR(ValidateEpsilon(epsilon));
RETURN_IF_ERROR(
- PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
- max_partitions_contributed));
+ ValidateMaxPartitionsContributed(max_partitions_contributed));
if (threshold < 1) {
return CalculateUnadjustedDelta(
@@ -380,11 +347,10 @@
static absl::StatusOr<double> CalculateThreshold(
double epsilon, double delta, int64_t max_partitions_contributed) {
- RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
- RETURN_IF_ERROR(PartitionSelectionStrategy::DeltaIsSetAndValid(delta));
+ RETURN_IF_ERROR(ValidateEpsilon(epsilon));
+ RETURN_IF_ERROR(ValidateDelta(delta));
RETURN_IF_ERROR(
- PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
- max_partitions_contributed));
+ ValidateMaxPartitionsContributed(max_partitions_contributed));
ASSIGN_OR_RETURN(double adjusted_delta,
CalculateAdjustedDelta(delta, max_partitions_contributed));
@@ -435,7 +401,7 @@
class GaussianPartitionSelection : public PartitionSelectionStrategy {
public:
// Builder for GaussianPartitionSelection
- class Builder : public PartitionSelectionStrategy::Builder {
+ class Builder : public PartitionSelectionStrategyBuilder {
public:
Builder& SetGaussianMechanism(
std::unique_ptr<GaussianMechanism::Builder> gaussian_builder) {
@@ -445,9 +411,10 @@
absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>> Build()
override {
- RETURN_IF_ERROR(EpsilonIsSetAndValid());
- RETURN_IF_ERROR(DeltaIsSetAndValid());
- RETURN_IF_ERROR(MaxPartitionsContributedIsSetAndValid());
+ RETURN_IF_ERROR(ValidateEpsilon(GetEpsilon()));
+ RETURN_IF_ERROR(ValidateDelta(GetDelta()));
+ RETURN_IF_ERROR(
+ ValidateMaxPartitionsContributed(GetMaxPartitionsContributed()));
if (gaussian_builder_ == nullptr) {
gaussian_builder_ = absl::make_unique<GaussianMechanism::Builder>();
}
@@ -512,11 +479,10 @@
static absl::StatusOr<double> CalculateThresholdDelta(
double epsilon, double noise_delta, double threshold,
int64_t max_partitions_contributed) {
- RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
- RETURN_IF_ERROR(DeltaIsSetAndValid(noise_delta));
+ RETURN_IF_ERROR(ValidateEpsilon(epsilon));
+ RETURN_IF_ERROR(ValidateDelta(noise_delta));
RETURN_IF_ERROR(
- PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
- max_partitions_contributed));
+ ValidateMaxPartitionsContributed(max_partitions_contributed));
double sigma = GaussianMechanism::CalculateStddev(
epsilon, noise_delta, max_partitions_contributed);
@@ -539,12 +505,11 @@
static absl::StatusOr<double> CalculateThreshold(
double epsilon, double noise_delta, double threshold_delta,
int64_t max_partitions_contributed) {
- RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
- RETURN_IF_ERROR(DeltaIsSetAndValid(noise_delta));
- RETURN_IF_ERROR(DeltaIsSetAndValid(threshold_delta));
+ RETURN_IF_ERROR(ValidateEpsilon(epsilon));
+ RETURN_IF_ERROR(ValidateDelta(noise_delta));
+ RETURN_IF_ERROR(ValidateDelta(threshold_delta));
RETURN_IF_ERROR(
- PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
- max_partitions_contributed));
+ ValidateMaxPartitionsContributed(max_partitions_contributed));
double sigma = GaussianMechanism::CalculateStddev(
epsilon, noise_delta, max_partitions_contributed);
@@ -580,7 +545,6 @@
double threshold_;
std::unique_ptr<NumericalMechanism> mechanism_;
};
-
} // namespace differential_privacy
#endif // DIFFERENTIAL_PRIVACY_CPP_ALGORITHMS_PARTITION_SELECTION_H_
diff --git a/cc/algorithms/partition-selection_test.cc b/cc/algorithms/partition-selection_test.cc
index f4c7a10..000e485 100644
--- a/cc/algorithms/partition-selection_test.cc
+++ b/cc/algorithms/partition-selection_test.cc
@@ -19,10 +19,12 @@
#include <cmath>
#include <cstdlib>
+#include "base/testing/status_matchers.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
#include "absl/status/statusor.h"
-#include "algorithms/numerical-mechanisms-testing.h"
+#include "algorithms/numerical-mechanisms.h"
namespace differential_privacy {
namespace {
@@ -30,7 +32,8 @@
using ::testing::DoubleEq;
using ::testing::DoubleNear;
using ::testing::Eq;
-using ::testing::MatchesRegex;
+using ::testing::HasSubstr;
+using ::differential_privacy::base::testing::StatusIs;
constexpr int kNumSamples = 10000000;
constexpr int kSmallNumSamples = 1000000;
@@ -52,143 +55,122 @@
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionUnsetEpsilon) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build =
- test_builder.SetDelta(0.1).SetMaxPartitionsContributed(2).Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Epsilon must be set.*"));
+ EXPECT_THAT(test_builder.SetDelta(0.1).SetMaxPartitionsContributed(2).Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Epsilon must be set")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionNanEpsilon) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(NAN)
- .SetDelta(0.3)
- .SetMaxPartitionsContributed(4)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Epsilon must be a valid numeric value.*"));
+ EXPECT_THAT(test_builder.SetEpsilon(NAN)
+ .SetDelta(0.3)
+ .SetMaxPartitionsContributed(4)
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Epsilon must be a valid numeric value")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionNotFiniteEpsilon) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build =
- test_builder.SetEpsilon(std::numeric_limits<double>::infinity())
- .SetDelta(0.3)
- .SetMaxPartitionsContributed(4)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Epsilon must be finite.*"));
+ EXPECT_THAT(test_builder.SetEpsilon(std::numeric_limits<double>::infinity())
+ .SetDelta(0.3)
+ .SetMaxPartitionsContributed(4)
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Epsilon must be finite")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionNegativeEpsilon) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(-5.0)
- .SetDelta(0.6)
- .SetMaxPartitionsContributed(7)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Epsilon must be finite and positive.*"));
+ EXPECT_THAT(test_builder.SetEpsilon(-5.0)
+ .SetDelta(0.6)
+ .SetMaxPartitionsContributed(7)
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Epsilon must be finite and positive")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionUnsetDelta) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build =
- test_builder.SetEpsilon(8.0).SetMaxPartitionsContributed(9).Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Delta must be set.*"));
+ EXPECT_THAT(
+ test_builder.SetEpsilon(8.0).SetMaxPartitionsContributed(9).Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be set.")));
}
TEST(PartitionSelectionTest, NearTruncatedGeometricPartitionSelectionNanDelta) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(1.2)
- .SetDelta(NAN)
- .SetMaxPartitionsContributed(3)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Delta must be a valid numeric value.*"));
+ EXPECT_THAT(test_builder.SetEpsilon(1.2)
+ .SetDelta(NAN)
+ .SetMaxPartitionsContributed(3)
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be a valid numeric value")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionNotFiniteDelta) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(1.2)
- .SetDelta(std::numeric_limits<double>::infinity())
- .SetMaxPartitionsContributed(3)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ EXPECT_THAT(test_builder.SetEpsilon(1.2)
+ .SetDelta(std::numeric_limits<double>::infinity())
+ .SetMaxPartitionsContributed(3)
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionInvalidDelta) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(4.5)
- .SetDelta(6.0)
- .SetMaxPartitionsContributed(7)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ EXPECT_THAT(test_builder.SetEpsilon(4.5)
+ .SetDelta(6.0)
+ .SetMaxPartitionsContributed(7)
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionUnsetMaxPartitionsContributed) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(0.8).SetDelta(0.9).Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be set.*"));
+ EXPECT_THAT(
+ test_builder.SetEpsilon(0.8).SetDelta(0.9).Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be set")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionNegativeMaxPartitionsContributed) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(0.1)
- .SetDelta(0.2)
- .SetMaxPartitionsContributed(-3)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be positive.*"));
+ EXPECT_THAT(
+ test_builder.SetEpsilon(0.1)
+ .SetDelta(0.2)
+ .SetMaxPartitionsContributed(-3)
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be positive, but is -3.")));
}
TEST(PartitionSelectionTest,
NearTruncatedGeometricPartitionSelectionZeroMaxPartitionsContributed) {
NearTruncatedGeometricPartitionSelection::Builder test_builder;
- auto failed_build = test_builder.SetEpsilon(0.1)
- .SetDelta(0.2)
- .SetMaxPartitionsContributed(0)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be positive.*"));
+ EXPECT_THAT(
+ test_builder.SetEpsilon(0.1)
+ .SetDelta(0.2)
+ .SetMaxPartitionsContributed(0)
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be positive, but is 0.")));
}
// We expect the probability of keeping a partition with one user
@@ -365,142 +347,124 @@
TEST(PartitionSelectionTest,
LaplacePartitionSelectionUnsetMaxPartitionsContributed) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetDelta(0.1)
.SetEpsilon(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be set.*"));
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be set")));
}
TEST(PartitionSelectionTest,
LaplacePartitionSelectionNegativeMaxPartitionsContributed) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetDelta(0.1)
.SetEpsilon(2)
.SetMaxPartitionsContributed(-3)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be positive.*"));
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be positive, but is -3.")));
}
TEST(PartitionSelectionTest,
LaplacePartitionSelectionZeroMaxPartitionsContributed) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetDelta(0.1)
.SetEpsilon(2)
.SetMaxPartitionsContributed(0)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be positive.*"));
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be positive, but is 0.")));
}
TEST(PartitionSelectionTest, LaplacePartitionSelectionUnsetEpsilon) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetDelta(0.1)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Epsilon must be set.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Epsilon must be set.")));
}
TEST(PartitionSelectionTest, LaplacePartitionSelectionUnsetDelta) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetEpsilon(0.1)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Delta must be set.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be set")));
}
TEST(PartitionSelectionTest, LaplacePartitionSelectionNanDelta) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(NAN)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Delta must be a valid numeric value.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be a valid numeric value")));
}
TEST(PartitionSelectionTest, LaplacePartitionSelectionNotFiniteDelta) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(std::numeric_limits<double>::infinity())
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
TEST(PartitionSelectionTest, LaplacePartitionSelectionInvalidPositiveDelta) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(5.2)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
TEST(PartitionSelectionTest, LaplacePartitionSelectionInvalidNegativeDelta) {
LaplacePartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(-0.1)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
// We expect the probability of keeping a partition with one user
@@ -1124,142 +1088,124 @@
TEST(PartitionSelectionTest,
GaussianPartitionSelectionUnsetMaxPartitionsContributed) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetDelta(0.1)
.SetEpsilon(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be set.*"));
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be set")));
}
TEST(PartitionSelectionTest,
GaussianPartitionSelectionNegativeMaxPartitionsContributed) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetDelta(0.1)
.SetEpsilon(2)
.SetMaxPartitionsContributed(-3)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be positive.*"));
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be positive")));
}
TEST(PartitionSelectionTest,
GaussianPartitionSelectionZeroMaxPartitionsContributed) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetDelta(0.1)
.SetEpsilon(2)
.SetMaxPartitionsContributed(0)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
- " contribute to must be positive.*"));
+ .Build(),
+ StatusIs(
+ absl::StatusCode::kInvalidArgument,
+ HasSubstr("Maximum number of partitions that can be contributed to "
+ "(i.e., L0 sensitivity) must be positive, but is 0.")));
}
TEST(PartitionSelectionTest, GaussianPartitionSelectionUnsetEpsilon) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetDelta(0.1)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Epsilon must be set.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Epsilon must be set.")));
}
TEST(PartitionSelectionTest, GaussianPartitionSelectionUnsetDelta) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetEpsilon(0.1)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Delta must be set.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be set.")));
}
TEST(PartitionSelectionTest, GaussianPartitionSelectionNanDelta) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(NAN)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message, MatchesRegex("^Delta must be a valid numeric value.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be a valid numeric value")));
}
TEST(PartitionSelectionTest, GaussianPartitionSelectionNotFiniteDelta) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(std::numeric_limits<double>::infinity())
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
TEST(PartitionSelectionTest, GaussianPartitionSelectionInvalidPositiveDelta) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(5.2)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
TEST(PartitionSelectionTest, GaussianPartitionSelectionInvalidNegativeDelta) {
GaussianPartitionSelection::Builder test_builder;
- auto failed_build =
+ EXPECT_THAT(
test_builder
.SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
.SetEpsilon(0.1)
.SetDelta(-0.1)
.SetMaxPartitionsContributed(2)
- .Build();
- EXPECT_THAT(failed_build.status().code(),
- Eq(absl::StatusCode::kInvalidArgument));
- std::string message(std::string(failed_build.status().message()));
- EXPECT_THAT(message,
- MatchesRegex("^Delta must be in the inclusive interval.*"));
+ .Build(),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("Delta must be in the inclusive interval")));
}
TEST(PartitionSelectionTest, CalculateGaussianThresholdTests) {
diff --git a/cc/algorithms/util.cc b/cc/algorithms/util.cc
index fb6dfe9..6d3fae0 100644
--- a/cc/algorithms/util.cc
+++ b/cc/algorithms/util.cc
@@ -18,6 +18,7 @@
#include <cmath>
#include <cstdlib>
+#include <memory>
#include <vector>
#include "absl/status/status.h"
@@ -323,5 +324,4 @@
return ValidateIsGreaterThanOrEqualTo(branching_factor, /*lower_bound=*/2,
"Branching Factor");
}
-
} // namespace differential_privacy
diff --git a/cc/algorithms/util.h b/cc/algorithms/util.h
index 9aeb72c..ceb99fc 100644
--- a/cc/algorithms/util.h
+++ b/cc/algorithms/util.h
@@ -546,7 +546,6 @@
}
return absl::OkStatus();
}
-
} // namespace differential_privacy
#endif // DIFFERENTIAL_PRIVACY_ALGORITHMS_UTIL_H_
diff --git a/go/go.sum b/go/go.sum
new file mode 100644
index 0000000..d88af24
--- /dev/null
+++ b/go/go.sum
@@ -0,0 +1,29 @@
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
+github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/grd/stat v0.0.0-20130623202159-138af3fd5012 h1:TVY1GBBIAAph4RWO9Y3p1wU+7n6khY1jxPKjDphzznA=
+github.com/grd/stat v0.0.0-20130623202159-138af3fd5012/go.mod h1:hHyH5N67TF4tD4PBbqMlyuIu5Lq5QwKSgNyyG31trzY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=
+golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o=
+gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY=
\ No newline at end of file
diff --git a/java/dp_java_deps_preload.bzl b/java/dp_java_deps_preload.bzl
index 1a80d57..20e21bf 100644
--- a/java/dp_java_deps_preload.bzl
+++ b/java/dp_java_deps_preload.bzl
@@ -23,4 +23,3 @@
sha256 = BAZEL_COMMON_SHA,
strip_prefix = "bazel-common-%s" % BAZEL_COMMON_TAG,
)
-
diff --git a/python/dp_accounting/mechanism_calibration.py b/python/dp_accounting/mechanism_calibration.py
index 93dafba..055788c 100644
--- a/python/dp_accounting/mechanism_calibration.py
+++ b/python/dp_accounting/mechanism_calibration.py
@@ -106,10 +106,9 @@
Callable[[int], dp_event.DpEvent]],
target_epsilon: float,
target_delta: float,
- bracket_interval: BracketInterval,
+ bracket_interval: Optional[BracketInterval] = None,
discrete: bool = False,
- tol: Optional[float] = None
-) -> Union[float, int]:
+ tol: Optional[float] = None) -> Union[float, int]:
"""Searches for optimal mechanism parameter value within privacy budget.
The procedure searches over the space of parameters by creating, for each
@@ -131,7 +130,8 @@
target_epsilon: The target epsilon value.
target_delta: The target delta value.
bracket_interval: A BracketInterval used to determine the upper and lower
- endpoints of the interval within which Brent's method will search.
+ endpoints of the interval within which Brent's method will search. If
+ None, searches for a non-negative bracket starting from [0, 1].
discrete: A bool determining whether the parameter is continuous or discrete
valued. If True, the parameter is assumed to take only integer values.
Concretely, `discrete=True` has three effects. 1) ints, not floats are
@@ -173,6 +173,9 @@
raise ValueError(f'target_delta must be in range [0, 1]. Found '
f'{target_delta}.')
+ if bracket_interval is None:
+ bracket_interval = LowerEndpointAndGuess(0, 1)
+
if tol is None:
tol = 0.5 if discrete else 1e-6
elif discrete:
@@ -196,19 +199,17 @@
raise TypeError(f'Unrecognized bracket_interval type: '
f'{type(bracket_interval)}')
- value_1 = epsilon_gap(bracket_interval.endpoint_1)
- value_2 = epsilon_gap(bracket_interval.endpoint_2)
- if value_1 * value_2 > 0:
+ try:
+ root, result = optimize.brentq(
+ epsilon_gap,
+ bracket_interval.endpoint_1,
+ bracket_interval.endpoint_2,
+ xtol=tol,
+ full_output=True)
+ except ValueError as err:
raise ValueError(
- f'Bracket endpoints do not bracket target_epsilon={target_epsilon}: '
- f'endpoint 1 {bracket_interval.endpoint_1} with epsilon='
- f'{value_1 + target_epsilon}, and endpoint 2 '
- f'{bracket_interval.endpoint_2} with epsilon={value_2 + target_epsilon}'
- )
-
- root, result = optimize.brentq(epsilon_gap, bracket_interval.endpoint_1,
- bracket_interval.endpoint_2, xtol=tol,
- full_output=True)
+ '`brentq` raised ValueError. This often means the supplied bracket '
+ f'interval {bracket_interval} did not bracket a solution.') from err
if not result.converged:
raise NoOptimumFoundError(
diff --git a/python/dp_accounting/mechanism_calibration_test.py b/python/dp_accounting/mechanism_calibration_test.py
index 5975d3e..ad4b86d 100644
--- a/python/dp_accounting/mechanism_calibration_test.py
+++ b/python/dp_accounting/mechanism_calibration_test.py
@@ -167,12 +167,12 @@
mechanism_calibration.ExplicitBracketInterval(0, 5))
def test_bad_bracket_interval(self):
- with self.assertRaisesRegex(ValueError, 'Bracket endpoints'):
+ with self.assertRaisesRegex(ValueError, 'did not bracket a solution'):
mechanism_calibration.calibrate_dp_mechanism(
lambda: MockAccountant(lambda x: x), MockEvent, 1.0, 0.0,
mechanism_calibration.ExplicitBracketInterval(2, 5))
- with self.assertRaisesRegex(ValueError, 'Bracket endpoints'):
+ with self.assertRaisesRegex(ValueError, 'did not bracket a solution'):
mechanism_calibration.calibrate_dp_mechanism(
lambda: MockAccountant(lambda x: x), MockEvent, 1.0, 0.0,
mechanism_calibration.ExplicitBracketInterval(-2, 0))
diff --git a/python/dp_accounting/pld/pld_privacy_accountant.py b/python/dp_accounting/pld/pld_privacy_accountant.py
index 91d620c..81afbfc 100644
--- a/python/dp_accounting/pld/pld_privacy_accountant.py
+++ b/python/dp_accounting/pld/pld_privacy_accountant.py
@@ -57,18 +57,24 @@
return None
elif isinstance(event, dp_event.GaussianDpEvent):
if do_compose:
- gaussian_pld = PLD.from_gaussian_mechanism(
- standard_deviation=event.noise_multiplier / math.sqrt(count),
- value_discretization_interval=self._value_discretization_interval)
- self._pld = self._pld.compose(gaussian_pld)
+ if event.noise_multiplier == 0:
+ self._contains_non_dp_event = True
+ else:
+ gaussian_pld = PLD.from_gaussian_mechanism(
+ standard_deviation=event.noise_multiplier / math.sqrt(count),
+ value_discretization_interval=self._value_discretization_interval)
+ self._pld = self._pld.compose(gaussian_pld)
return None
elif isinstance(event, dp_event.LaplaceDpEvent):
if do_compose:
- laplace_pld = PLD.from_laplace_mechanism(
- parameter=event.noise_multiplier,
- value_discretization_interval=self._value_discretization_interval
- ).self_compose(count)
- self._pld = self._pld.compose(laplace_pld)
+ if event.noise_multiplier == 0:
+ self._contains_non_dp_event = True
+ else:
+ laplace_pld = PLD.from_laplace_mechanism(
+ parameter=event.noise_multiplier,
+ value_discretization_interval=self._value_discretization_interval
+ ).self_compose(count)
+ self._pld = self._pld.compose(laplace_pld)
return None
elif isinstance(event, dp_event.PoissonSampledDpEvent):
if self.neighboring_relation != NeighborRel.ADD_OR_REMOVE_ONE:
@@ -79,19 +85,31 @@
invalid_event=event, error_message=error_msg)
if isinstance(event.event, dp_event.GaussianDpEvent):
if do_compose:
- subsampled_gaussian_pld = PLD.from_gaussian_mechanism(
- standard_deviation=event.event.noise_multiplier,
- value_discretization_interval=self._value_discretization_interval,
- sampling_prob=event.sampling_probability).self_compose(count)
- self._pld = self._pld.compose(subsampled_gaussian_pld)
+ if event.sampling_probability == 0:
+ pass
+ elif event.event.noise_multiplier == 0:
+ self._contains_non_dp_event = True
+ else:
+ subsampled_gaussian_pld = PLD.from_gaussian_mechanism(
+ standard_deviation=event.event.noise_multiplier,
+ value_discretization_interval=self
+ ._value_discretization_interval,
+ sampling_prob=event.sampling_probability).self_compose(count)
+ self._pld = self._pld.compose(subsampled_gaussian_pld)
return None
elif isinstance(event.event, dp_event.LaplaceDpEvent):
if do_compose:
- subsampled_laplace_pld = PLD.from_laplace_mechanism(
- parameter=event.event.noise_multiplier,
- value_discretization_interval=self._value_discretization_interval,
- sampling_prob=event.sampling_probability).self_compose(count)
- self._pld = self._pld.compose(subsampled_laplace_pld)
+ if event.sampling_probability == 0:
+ pass
+ elif event.event.noise_multiplier == 0:
+ self._contains_non_dp_event = True
+ else:
+ subsampled_laplace_pld = PLD.from_laplace_mechanism(
+ parameter=event.event.noise_multiplier,
+ value_discretization_interval=self
+ ._value_discretization_interval,
+ sampling_prob=event.sampling_probability).self_compose(count)
+ self._pld = self._pld.compose(subsampled_laplace_pld)
return None
else:
return CompositionErrorDetails(
diff --git a/python/dp_accounting/pld/pld_privacy_accountant_test.py b/python/dp_accounting/pld/pld_privacy_accountant_test.py
index 784f02b..9cee09a 100644
--- a/python/dp_accounting/pld/pld_privacy_accountant_test.py
+++ b/python/dp_accounting/pld/pld_privacy_accountant_test.py
@@ -51,6 +51,28 @@
with self.assertRaises(ValueError):
accountant.compose(event, count)
+ @parameterized.parameters(
+ dp_event.GaussianDpEvent(0),
+ dp_event.LaplaceDpEvent(0),
+ dp_event.PoissonSampledDpEvent(0.1, dp_event.GaussianDpEvent(0)),
+ dp_event.PoissonSampledDpEvent(0.1, dp_event.LaplaceDpEvent(0)))
+ def test_additive_noise_mechanisms_with_zero_noise_multiplier(self, event):
+ accountant = pld_privacy_accountant.PLDAccountant()
+ accountant.compose(event)
+ self.assertEqual(accountant.get_delta(1.0), 1)
+ self.assertEqual(accountant.get_epsilon(0.01), math.inf)
+
+ @parameterized.parameters(
+ dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(1)),
+ dp_event.PoissonSampledDpEvent(0, dp_event.LaplaceDpEvent(1)),
+ dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(0)),
+ dp_event.PoissonSampledDpEvent(0, dp_event.LaplaceDpEvent(0)))
+ def test_poisson_subsampling_with_zero_probability(self, event):
+ accountant = pld_privacy_accountant.PLDAccountant()
+ accountant.compose(event)
+ self.assertEqual(accountant.get_delta(0), 0)
+ self.assertEqual(accountant.get_epsilon(0), 0)
+
def test_gaussian_basic(self):
gaussian_event = dp_event.GaussianDpEvent(noise_multiplier=math.sqrt(3))
accountant = pld_privacy_accountant.PLDAccountant()
diff --git a/python/dp_accounting/rdp/rdp_privacy_accountant.py b/python/dp_accounting/rdp/rdp_privacy_accountant.py
index f1825fb..16335fe 100644
--- a/python/dp_accounting/rdp/rdp_privacy_accountant.py
+++ b/python/dp_accounting/rdp/rdp_privacy_accountant.py
@@ -347,12 +347,12 @@
"""
def compute_one_order(q, alpha):
- if np.isinf(alpha) or noise_multiplier == 0:
- return np.inf
-
if q == 0:
return 0
+ if np.isinf(alpha) or noise_multiplier == 0:
+ return np.inf
+
if q == 1.:
return alpha / (2 * noise_multiplier**2)
diff --git a/python/dp_accounting/rdp/rdp_privacy_accountant_test.py b/python/dp_accounting/rdp/rdp_privacy_accountant_test.py
index b6291b1..1e60ea8 100644
--- a/python/dp_accounting/rdp/rdp_privacy_accountant_test.py
+++ b/python/dp_accounting/rdp/rdp_privacy_accountant_test.py
@@ -171,10 +171,12 @@
self.assertAlmostEqual(rdp_with_heterogeneous_compose,
base_rdp + base_rdp_2)
- def test_zero_poisson_sample(self):
+ @parameterized.parameters(
+ dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(1.0)),
+ dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(0.0)))
+ def test_zero_poisson_sample(self, event):
accountant = rdp_privacy_accountant.RdpAccountant([3.14159])
- accountant.compose(
- dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(1.0)))
+ accountant.compose(event)
self.assertEqual(accountant.get_epsilon(1e-10), 0)
self.assertEqual(accountant.get_delta(1e-10), 0)