Skip to content

Commit

Permalink
C++: Fix multiple contributions for implicit ApproxBounds (#87)
Browse files Browse the repository at this point in the history
This is a backport of the fix in the main branch
ae9c6b6

This change contains a critical fix for cases where ApproxBounds is used
implicitly (not specified in the builder) and we use multiple
contributions per user or multiple contributions per partition for sum,
mean, variance, and stddev.
  • Loading branch information
dibakch committed Oct 28, 2021
1 parent 8d1c557 commit ec0b439
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 0 deletions.
4 changes: 4 additions & 0 deletions cc/algorithms/approx-bounds.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ class ApproxBounds : public Algorithm<T> {
return report;
}

// Returns a pointer to the mechanism for testing. Does not transfer
// ownership.
NumericalMechanism* GetMechanismForTesting() { return mechanism_.get(); }

protected:
ApproxBounds(double epsilon, int64_t num_bins, double scale, double base,
double k, bool preset_k,
Expand Down
8 changes: 8 additions & 0 deletions cc/algorithms/bounded-algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,18 @@ class BoundedAlgorithmBuilder : public AlgorithmBuilder<T, Algorithm, Builder> {
remaining_epsilon_ =
AlgorithmBuilder::GetEpsilon().value() - bounds_epsilon;
auto mech_builder = AlgorithmBuilder::GetMechanismBuilderClone();
const int max_partitions_contributed =
AlgorithmBuilder::GetMaxPartitionsContributed().value_or(1);
const int max_contributions_per_partition =
AlgorithmBuilder::GetMaxContributionsPerPartition().value_or(1);
ASSIGN_OR_RETURN(approx_bounds_,
typename ApproxBounds<T>::Builder()
.SetEpsilon(bounds_epsilon)
.SetLaplaceMechanism(std::move(mech_builder))
.SetMaxPartitionsContributed(
max_partitions_contributed)
.SetMaxContributionsPerPartition(
max_contributions_per_partition)
.Build());
}
return absl::OkStatus();
Expand Down
3 changes: 3 additions & 0 deletions cc/algorithms/bounded-sum.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ class BoundedSumWithApproxBounds : public BoundedSum<T> {
return memory;
}

// Returns the ApproxBounds for testing. Does not transfer ownership.
ApproxBounds<T>* GetApproxBoundsForTesting() { return approx_bounds_.get(); }

protected:
base::StatusOr<Output> GenerateResult(double privacy_budget,
double noise_interval_level) override {
Expand Down
34 changes: 34 additions & 0 deletions cc/algorithms/bounded-sum_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ namespace differential_privacy {
namespace {

using ::differential_privacy::test_utils::ZeroNoiseMechanism;
using ::testing::DoubleEq;
using ::testing::Eq;
using ::testing::NotNull;
using ::differential_privacy::base::testing::EqualsProto;
using ::testing::HasSubstr;
using ::differential_privacy::base::testing::IsOkAndHolds;
Expand Down Expand Up @@ -804,5 +806,37 @@ TYPED_TEST(BoundedSumTest, SplitsEpsilonWithAutomaticBounds) {
EXPECT_LT(sum_with_approx_bounds->GetAggregationEpsilon(), epsilon);
}

TEST(BoundedSumTest, ApproxBoundsMechanismHasExpectedVariance) {
const double epsilon = 1.0;
const int max_partitions_contributed = 2;
const int max_contributions_per_partition = 3;

base::StatusOr<std::unique_ptr<BoundedSum<double>>> bs =
BoundedSum<double>::Builder()
.SetEpsilon(epsilon)
.SetMaxPartitionsContributed(max_partitions_contributed)
.SetMaxContributionsPerPartition(max_contributions_per_partition)
.Build();
ASSERT_OK(bs);

auto *bs_with_approx_bounds =
static_cast<BoundedSumWithApproxBounds<double>*>(bs.value().get());
ASSERT_THAT(bs_with_approx_bounds, NotNull());

const double expected_variance =
LaplaceMechanism::Builder()
.SetEpsilon(epsilon)
.SetL0Sensitivity(max_partitions_contributed)
.SetLInfSensitivity(max_contributions_per_partition)
.Build()
.value()
->GetVariance();

EXPECT_THAT(bs_with_approx_bounds->GetApproxBoundsForTesting()
->GetMechanismForTesting()
->GetVariance(),
DoubleEq(expected_variance));
}

} // namespace
} // namespace differential_privacy

0 comments on commit ec0b439

Please sign in to comment.