blob: ec3acb6dda1b54a84c9007635466fed50fadb442 [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_COUNT_H_
#define DIFFERENTIAL_PRIVACY_ALGORITHMS_COUNT_H_
#include <memory>
#include <optional>
#include <utility>
#include <cstdint>
#include "google/protobuf/any.pb.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "algorithms/algorithm.h"
#include "algorithms/numerical-mechanisms.h"
#include "algorithms/util.h"
#include "proto/util.h"
#include "proto/confidence-interval.pb.h"
#include "proto/data.pb.h"
#include "proto/summary.pb.h"
#include "base/status_macros.h"
namespace differential_privacy {
// Count the number of elements in a set, with differentially private noise.
template <typename T>
class Count : public Algorithm<T> {
public:
class Builder;
void AddEntry(const T& v) override { AddMultipleEntries(v, 1); }
absl::StatusOr<ConfidenceInterval> NoiseConfidenceInterval(
double confidence_level) override {
return mechanism_->NoiseConfidenceInterval(confidence_level);
}
// Create and return summary containing the count.
Summary Serialize() const override {
// Create CountSummary.
CountSummary count_summ;
count_summ.set_count(count_);
// Create Summary.
Summary summary;
summary.mutable_data()->PackFrom(count_summ);
return summary;
}
// Add count from serialized data.
absl::Status Merge(const Summary& summary) override {
if (!summary.has_data()) {
return absl::InternalError("Cannot merge summary with no count data.");
}
// Add counts.
CountSummary count_summary;
if (!summary.data().UnpackTo(&count_summary)) {
return absl::InternalError("Count summary unable to be unpacked.");
}
count_ += count_summary.count();
return absl::OkStatus();
}
int64_t MemoryUsed() override {
int64_t memory = sizeof(Count<T>);
if (mechanism_) {
memory += mechanism_->MemoryUsed();
}
return memory;
}
protected:
absl::StatusOr<Output> GenerateResult(double noise_interval_level) override {
Output output;
int64_t countWithNoise = mechanism_->AddNoise(count_);
absl::StatusOr<ConfidenceInterval> interval =
NoiseConfidenceInterval(noise_interval_level);
if (interval.ok()) {
output = MakeOutput<int64_t>(countWithNoise, interval.value());
} else {
output = MakeOutput<int64_t>(countWithNoise);
}
return output;
}
void ResetState() override { count_ = 0; }
int64_t GetCount() const { return count_; }
// The constructor and count_ are non-private for testing.
Count(double epsilon, double delta,
std::unique_ptr<NumericalMechanism> mechanism)
: Algorithm<T>(epsilon, delta),
count_(0),
mechanism_(std::move(mechanism)) {}
private:
void AddMultipleEntries(const T& v, int64_t num_of_entries) {
absl::Status status =
ValidateIsNonNegative(num_of_entries, "Number of entries");
if (!status.ok()) {
return;
}
count_ += num_of_entries;
}
// Friend class for testing only
friend class CountTestPeer;
// Count of the number of entries added.
int64_t count_;
std::unique_ptr<NumericalMechanism> mechanism_;
};
template <typename T>
class Count<T>::Builder {
public:
Count<T>::Builder& SetEpsilon(double epsilon) {
epsilon_ = epsilon;
return *this;
}
Count<T>::Builder& SetDelta(double delta) {
delta_ = delta;
return *this;
}
Count<T>::Builder& SetMaxPartitionsContributed(
int max_partitions_contributed) {
max_partitions_contributed_ = max_partitions_contributed;
return *this;
}
Count<T>::Builder& SetMaxContributionsPerPartition(
int max_contributions_per_partition) {
max_contributions_per_partition_ = max_contributions_per_partition;
return *this;
}
Count<T>::Builder& SetLaplaceMechanism(
std::unique_ptr<NumericalMechanismBuilder> mechanism_builder) {
mechanism_builder_ = std::move(mechanism_builder);
return *this;
}
absl::StatusOr<std::unique_ptr<Count<T>>> Build() {
RETURN_IF_ERROR(ValidateEpsilon(epsilon_));
RETURN_IF_ERROR(ValidateDelta(delta_));
RETURN_IF_ERROR(
ValidateMaxPartitionsContributed(max_partitions_contributed_));
RETURN_IF_ERROR(
ValidateMaxContributionsPerPartition(max_contributions_per_partition_));
ASSIGN_OR_RETURN(std::unique_ptr<NumericalMechanism> count_mechanism,
BuildCountMechanism());
return absl::WrapUnique(
new Count<T>(epsilon_.value(), delta_, std::move(count_mechanism)));
}
private:
absl::optional<double> epsilon_;
double delta_ = 0;
int max_partitions_contributed_ = 1;
int max_contributions_per_partition_ = 1;
std::unique_ptr<NumericalMechanismBuilder> mechanism_builder_ =
std::make_unique<LaplaceMechanism::Builder>();
absl::StatusOr<std::unique_ptr<NumericalMechanism>> BuildCountMechanism() {
return mechanism_builder_->Clone()
->SetEpsilon(epsilon_.value())
.SetDelta(delta_)
.SetL0Sensitivity(max_partitions_contributed_)
.SetLInfSensitivity(max_contributions_per_partition_)
.Build();
}
};
} // namespace differential_privacy
#endif // DIFFERENTIAL_PRIVACY_ALGORITHMS_COUNT_H_