Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 1 addition & 61 deletions src/bindings/PyDP/algorithms/bounded_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,67 +22,7 @@ namespace dp = differential_privacy;
template <typename T, class Algorithm>
void declareBoundedAlgorithm(py::module& m) {
using builder = typename dp::python::AlgorithmBuilder<T, Algorithm>;
py::class_<Algorithm> bld(m, builder().get_algorithm_name().c_str());
bld.attr("__module__") = "pydp";
bld.def(py::init([](double epsilon, T lower_bound, T upper_bound, int l0_sensitivity,
int linf_sensitivity) {
py::print("Building with bounds");
return builder().Build(epsilon, lower_bound, upper_bound, l0_sensitivity,
linf_sensitivity);
}),
py::arg("epsilon"), py::arg("lower_bound"), py::arg("upper_bound"),
py::arg("l0_sensitivity") = 1, py::arg("linf_sensitivity") = 1);

bld.def(py::init([](double epsilon, int l0_sensitivity, int linf_sensitivity) {
py::print("Building without bounds");
return builder().Build(epsilon, nullopt, nullopt, l0_sensitivity,
linf_sensitivity);
}),
py::arg("epsilon"), py::arg("l0_sensitivity") = 1,
py::arg("linf_sensitivity") = 1);

bld.def_property_readonly("epsilon", [](Algorithm& obj) { return obj.GetEpsilon(); });

bld.def("privacy_budget_left",
[](Algorithm& obj) { return obj.RemainingPrivacyBudget(); });

bld.def("add_entry", [](Algorithm& obj, T& v) {
obj.AddEntry(v);
});

bld.def("add_entries", [](Algorithm& obj, std::vector<T>& v) {
obj.AddEntries(v.begin(), v.end());
});

bld.def("partial_result", [](Algorithm& obj) {
auto result = obj.PartialResult();

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}
return dp::GetValue<double>(result.ValueOrDie());
});

bld.def("partial_result", [](Algorithm& obj, double privacy_budget) {
if (privacy_budget > obj.RemainingPrivacyBudget()){
throw std::runtime_error("Privacy budget requeted exceeds set privacy budget");
}
auto result = obj.PartialResult(privacy_budget);

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}
return dp::GetValue<double>(result.ValueOrDie());
});

bld.def("result", [](Algorithm& obj, std::vector<T>& v) {
auto result = obj.Result(v.begin(), v.end());

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}
return dp::GetValue<double>(result.ValueOrDie());
});
builder().declare(m);
}

void init_algorithms_bounded_functions(py::module& m) {
Expand Down
45 changes: 6 additions & 39 deletions src/bindings/PyDP/algorithms/count.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,13 @@ using namespace std;
namespace py = pybind11;
namespace dp = differential_privacy;

template <typename T>
void declareCount(py::module& m, string const& suffix) {
using builder = typename dp::python::AlgorithmBuilder<T, dp::Count<T>>;

py::class_<dp::Count<T>> count(m, ("Count" + suffix).c_str());
count.attr("__module__") = "pydp";
count.def(py::init([](double epsilon) { return builder().Build(epsilon); }))
.def("add_entry", &dp::Count<T>::AddEntry)
.def("add_entries",
[](dp::Count<T>& obj, std::vector<T>& v) {
return obj.AddEntries(v.begin(), v.end());
})
// TODO: port ConfidenceInterval and Summary
//.def("noise_confidence_interval", &dp::Count<T>::NoiseConfidenceInterval)
//.def("serialize", &dp::Count<T>::Serialize)
//.def("merge", &dp::Count<T>::Merge)
.def("memory_used", &dp::Count<T>::MemoryUsed)
.def_property_readonly("epsilon",
[](dp::Count<T>& obj) { return obj.GetEpsilon(); })
.def("result",
[](dp::Count<T>& obj, std::vector<T>& v) {
auto result = obj.Result(v.begin(), v.end());

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}

return dp::GetValue<T>(result.ValueOrDie());
})
.def("partial_result",
[](dp::Count<T>& obj) {
return dp::GetValue<T>(obj.PartialResult().ValueOrDie());
})

.def("partial_result", [](dp::Count<T>& obj, double privacy_budget) {
return dp::GetValue<T>(obj.PartialResult(privacy_budget).ValueOrDie());
});
template <typename T, class Algorithm>
void declareAlgorithm(py::module& m) {
using builder = typename dp::python::AlgorithmBuilder<T, Algorithm>;
builder().declare(m);
}

void init_algorithms_count(py::module& m) {
declareCount<int>(m, "Int");
declareCount<double>(m, "Double");
declareAlgorithm<int, dp::Count<int>>(m);
declareAlgorithm<double, dp::Count<double>>(m);
}
116 changes: 114 additions & 2 deletions src/bindings/PyDP/pydp_lib/algorithm_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
#include "algorithms/bounded-standard-deviation.h"
#include "algorithms/bounded-sum.h"
#include "algorithms/bounded-variance.h"
#include "algorithms/count.h"
#include "algorithms/numerical-mechanisms.h"
#include "base/statusor.h"

namespace dp = differential_privacy;
namespace py = pybind11;

namespace differential_privacy {
namespace python {
Expand All @@ -25,7 +27,8 @@ constexpr bool is_bounded_algorithm() {
template <typename T, class Algorithm>
class AlgorithmBuilder {
public:
std::unique_ptr<Algorithm> Build(double epsilon,
std::unique_ptr<Algorithm> build(double epsilon,
std::optional<double> delta = std::nullopt,
std::optional<T> lower_bound = std::nullopt,
std::optional<T> upper_bound = std::nullopt,
std::optional<int> l0_sensitivity = std::nullopt,
Expand All @@ -34,6 +37,7 @@ class AlgorithmBuilder {

builder.SetEpsilon(epsilon);

if (delta.has_value()) builder.SetDelta(delta.value());
if (l0_sensitivity.has_value())
builder.SetMaxPartitionsContributed(l0_sensitivity.value());
if (linf_sensitivity.has_value())
Expand All @@ -58,12 +62,120 @@ class AlgorithmBuilder {
{typeid(dp::BoundedMean<T>), "BoundedMean"},
{typeid(dp::BoundedSum<T>), "BoundedSum"},
{typeid(dp::BoundedStandardDeviation<T>), "BoundedStandardDeviation"},
{typeid(dp::BoundedVariance<T>), "BoundedVariance"}};
{typeid(dp::BoundedVariance<T>), "BoundedVariance"},
{typeid(dp::Count<T>), "Count"}};

std::string get_algorithm_name() {
// Set the suffix string
return (algorithm_to_name[typeid(Algorithm)] + type_to_name[typeid(T)]);
}

void declare(py::module& m) {
py::class_<Algorithm> pyself(m, get_algorithm_name().c_str());

pyself.attr("__module__") = "pydp";

// Constructors
if constexpr (is_bounded_algorithm<T, Algorithm>()) {
// Explicit bounds constructor
pyself.def(
py::init([this](double epsilon, double delta, T lower_bound, T upper_bound,
int l0_sensitivity, int linf_sensitivity) {
return this->build(epsilon, delta, lower_bound, upper_bound, l0_sensitivity,
linf_sensitivity);
}),
py::arg("epsilon"), py::arg("delta") = 0, py::arg("lower_bound"),
py::arg("upper_bound"), py::arg("l0_sensitivity") = 1,
py::arg("linf_sensitivity") = 1);
}

// No bounds constructor
pyself.def(py::init([this](double epsilon, double delta, int l0_sensitivity,
int linf_sensitivity) {
return this->build(epsilon, delta, std::nullopt /*lower_bound*/,
std::nullopt /*upper_bound*/, l0_sensitivity,
linf_sensitivity);
}),
py::arg("epsilon"), py::arg("delta") = 0, py::arg("l0_sensitivity") = 1,
py::arg("linf_sensitivity") = 1);

// Getters
pyself.def_property_readonly("epsilon", &Algorithm::GetEpsilon);

pyself.def("privacy_budget_left", &Algorithm::RemainingPrivacyBudget);

pyself.def("consume_privacy_budget", &Algorithm::ConsumePrivacyBudget);

pyself.def("memory_used", &Algorithm::MemoryUsed);

// Input data
pyself.def("add_entries", [](Algorithm& pythis, std::vector<T>& v) {
pythis.AddEntries(v.begin(), v.end());
});

pyself.def("add_entry", &Algorithm::AddEntry);

// Compute results
pyself.def("result", [](Algorithm& pythis, std::vector<T>& v) {
auto result = pythis.Result(v.begin(), v.end());

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}

return dp::GetValue<T>(result.ValueOrDie());
});

pyself.def("partial_result", [](Algorithm& pythis) {
auto result = pythis.PartialResult();

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}

return dp::GetValue<double>(result.ValueOrDie());
});

pyself.def("partial_result", [](Algorithm& pythis, double privacy_budget) {
if (privacy_budget > pythis.RemainingPrivacyBudget()) {
throw std::runtime_error("Privacy budget requeted exceeds set privacy budget");
}

auto result = pythis.PartialResult(privacy_budget);

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}

return dp::GetValue<double>(result.ValueOrDie());
});

pyself.def("partial_result", [](Algorithm& pythis, double privacy_budget,
double noise_interval_level) {
if (privacy_budget > pythis.RemainingPrivacyBudget()) {
throw std::runtime_error("Privacy budget requeted exceeds set privacy budget");
}

auto result = pythis.PartialResult(privacy_budget, noise_interval_level);

if (!result.ok()) {
throw std::runtime_error(result.status().error_message());
}

return dp::GetValue<double>(result.ValueOrDie());
});

// Other methods
pyself.def("consume_privacy_budget", &Algorithm::ConsumePrivacyBudget);

pyself.def("reset", &Algorithm::Reset);

pyself.def("serialize", &Algorithm::Serialize);

pyself.def("merge", &Algorithm::Merge);

pyself.def("noise_confidence_interval", &Algorithm::NoiseConfidenceInterval);
}
};

} // namespace python
Expand Down