From ad2472518e077dcfd9db424adc68b5e80953a501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20S=C3=A1nchez=20Medina?= Date: Sun, 9 Aug 2020 10:54:49 +0100 Subject: [PATCH 1/4] Allow setting delta param --- src/bindings/PyDP/algorithms/bounded_functions.cpp | 6 +++--- src/bindings/PyDP/pydp_lib/algorithm_builder.hpp | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bindings/PyDP/algorithms/bounded_functions.cpp b/src/bindings/PyDP/algorithms/bounded_functions.cpp index 78cf748a..67f706d7 100644 --- a/src/bindings/PyDP/algorithms/bounded_functions.cpp +++ b/src/bindings/PyDP/algorithms/bounded_functions.cpp @@ -27,15 +27,15 @@ void declareBoundedAlgorithm(py::module& m) { 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); + return builder().Build(epsilon, nullopt, 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, + return builder().Build(epsilon, nullopt, nullopt, nullopt, l0_sensitivity, linf_sensitivity); }), py::arg("epsilon"), py::arg("l0_sensitivity") = 1, diff --git a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp index 32139ff7..d6eda33e 100644 --- a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp +++ b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp @@ -26,6 +26,7 @@ template class AlgorithmBuilder { public: std::unique_ptr Build(double epsilon, + std::optional delta = std::nullopt, std::optional lower_bound = std::nullopt, std::optional upper_bound = std::nullopt, std::optional l0_sensitivity = std::nullopt, @@ -34,6 +35,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()) From d4aa0a7ad6c2e3712bd4a0b936910e0b7911ce9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20S=C3=A1nchez=20Medina?= Date: Sun, 9 Aug 2020 14:53:29 +0100 Subject: [PATCH 2/4] Generalise algorithm declaration --- .../PyDP/algorithms/bounded_functions.cpp | 60 +--------- src/bindings/PyDP/algorithms/count.cpp | 45 +------- .../PyDP/pydp_lib/algorithm_builder.hpp | 105 +++++++++++++++++- 3 files changed, 109 insertions(+), 101 deletions(-) diff --git a/src/bindings/PyDP/algorithms/bounded_functions.cpp b/src/bindings/PyDP/algorithms/bounded_functions.cpp index 67f706d7..874d7f9c 100644 --- a/src/bindings/PyDP/algorithms/bounded_functions.cpp +++ b/src/bindings/PyDP/algorithms/bounded_functions.cpp @@ -22,65 +22,7 @@ namespace dp = differential_privacy; template void declareBoundedAlgorithm(py::module& m) { using builder = typename dp::python::AlgorithmBuilder; - py::class_ 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, nullopt, 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, 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_entries", [](Algorithm& obj, std::vector& 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(result.ValueOrDie()); - }); - - bld.def("partial_result", [](Algorithm& obj, double privacy_budget) { - auto result = obj.PartialResult(privacy_budget); - - if (!result.ok()) { - throw std::runtime_error(result.status().error_message()); - } - - return dp::GetValue(result.ValueOrDie()); - }); - - bld.def_property_readonly("epsilon", [](Algorithm& obj) { return obj.GetEpsilon(); }); - - bld.def("result", [](Algorithm& obj, std::vector& v) { - auto result = obj.Result(v.begin(), v.end()); - - if (!result.ok()) { - throw std::runtime_error(result.status().error_message()); - } - - return dp::GetValue(result.ValueOrDie()); - }); + builder().declare(m); } void init_algorithms_bounded_functions(py::module& m) { diff --git a/src/bindings/PyDP/algorithms/count.cpp b/src/bindings/PyDP/algorithms/count.cpp index 75d81b69..554fc030 100644 --- a/src/bindings/PyDP/algorithms/count.cpp +++ b/src/bindings/PyDP/algorithms/count.cpp @@ -12,46 +12,13 @@ using namespace std; namespace py = pybind11; namespace dp = differential_privacy; -template -void declareCount(py::module& m, string const& suffix) { - using builder = typename dp::python::AlgorithmBuilder>; - - py::class_> 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::AddEntry) - .def("add_entries", - [](dp::Count& obj, std::vector& v) { - return obj.AddEntries(v.begin(), v.end()); - }) - // TODO: port ConfidenceInterval and Summary - //.def("noise_confidence_interval", &dp::Count::NoiseConfidenceInterval) - //.def("serialize", &dp::Count::Serialize) - //.def("merge", &dp::Count::Merge) - .def("memory_used", &dp::Count::MemoryUsed) - .def_property_readonly("epsilon", - [](dp::Count& obj) { return obj.GetEpsilon(); }) - .def("result", - [](dp::Count& obj, std::vector& v) { - auto result = obj.Result(v.begin(), v.end()); - - if (!result.ok()) { - throw std::runtime_error(result.status().error_message()); - } - - return dp::GetValue(result.ValueOrDie()); - }) - .def("partial_result", - [](dp::Count& obj) { - return dp::GetValue(obj.PartialResult().ValueOrDie()); - }) - - .def("partial_result", [](dp::Count& obj, double privacy_budget) { - return dp::GetValue(obj.PartialResult(privacy_budget).ValueOrDie()); - }); +template +void declareAlgorithm(py::module& m) { + using builder = typename dp::python::AlgorithmBuilder; + builder().declare(m); } void init_algorithms_count(py::module& m) { - declareCount(m, "Int"); - declareCount(m, "Double"); + declareAlgorithm>(m); + declareAlgorithm>(m); } diff --git a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp index d6eda33e..4b168a23 100644 --- a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp +++ b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp @@ -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 { @@ -25,10 +27,10 @@ constexpr bool is_bounded_algorithm() { template class AlgorithmBuilder { public: - std::unique_ptr Build(double epsilon, - std::optional delta = std::nullopt, + std::unique_ptr build(double epsilon, std::optional lower_bound = std::nullopt, std::optional upper_bound = std::nullopt, + std::optional delta = std::nullopt, std::optional l0_sensitivity = std::nullopt, std::optional linf_sensitivity = std::nullopt) { auto builder = typename Algorithm::Builder(); @@ -60,12 +62,109 @@ class AlgorithmBuilder { {typeid(dp::BoundedMean), "BoundedMean"}, {typeid(dp::BoundedSum), "BoundedSum"}, {typeid(dp::BoundedStandardDeviation), "BoundedStandardDeviation"}, - {typeid(dp::BoundedVariance), "BoundedVariance"}}; + {typeid(dp::BoundedVariance), "BoundedVariance"}, + {typeid(dp::Count), "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_ pyself(m, get_algorithm_name().c_str()); + + pyself.attr("__module__") = "pydp"; + + // Constructors + if constexpr (is_bounded_algorithm()) { + pyself.def( + py::init([this](double epsilon, T lower_bound, T upper_bound, double delta, + int l0_sensitivity, int linf_sensitivity) { + return this->build(epsilon, std::nullopt, lower_bound, upper_bound, + l0_sensitivity, linf_sensitivity); + }), + py::arg("epsilon"), py::arg("lower_bound"), py::arg("upper_bound"), + py::arg("delta") = 0, py::arg("l0_sensitivity") = 1, + py::arg("linf_sensitivity") = 1); + } + + pyself.def(py::init([this](double epsilon, double delta, int l0_sensitivity, + int linf_sensitivity) { + return this->build(epsilon, std::nullopt, std::nullopt, std::nullopt, + 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& v) { + pythis.AddEntries(v.begin(), v.end()); + }); + + pyself.def("add_entry", &Algorithm::AddEntry); + + // Compute results + pyself.def("result", [](Algorithm& pythis, std::vector& v) { + auto result = pythis.Result(v.begin(), v.end()); + + if (!result.ok()) { + throw std::runtime_error(result.status().error_message()); + } + + return dp::GetValue(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(result.ValueOrDie()); + }); + + pyself.def("partial_result", [](Algorithm& pythis, double privacy_budget) { + auto result = pythis.PartialResult(privacy_budget); + + if (!result.ok()) { + throw std::runtime_error(result.status().error_message()); + } + + return dp::GetValue(result.ValueOrDie()); + }); + + pyself.def("partial_result", [](Algorithm& pythis, double privacy_budget, + double noise_interval_level) { + auto result = pythis.PartialResult(privacy_budget, noise_interval_level); + + if (!result.ok()) { + throw std::runtime_error(result.status().error_message()); + } + + return dp::GetValue(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 From cda79093750d16ec358405df60d01349edb2f131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20S=C3=A1nchez=20Medina?= Date: Sun, 9 Aug 2020 22:13:38 +0100 Subject: [PATCH 3/4] Fix delta arg position --- .../PyDP/pydp_lib/algorithm_builder.hpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp index cac20649..97e20e8f 100644 --- a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp +++ b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp @@ -28,9 +28,9 @@ template class AlgorithmBuilder { public: std::unique_ptr build(double epsilon, + std::optional delta = std::nullopt, std::optional lower_bound = std::nullopt, std::optional upper_bound = std::nullopt, - std::optional delta = std::nullopt, std::optional l0_sensitivity = std::nullopt, std::optional linf_sensitivity = std::nullopt) { auto builder = typename Algorithm::Builder(); @@ -77,21 +77,24 @@ class AlgorithmBuilder { // Constructors if constexpr (is_bounded_algorithm()) { + // Explicit bounds constructor pyself.def( - py::init([this](double epsilon, T lower_bound, T upper_bound, double delta, + py::init([this](double epsilon, double delta, T lower_bound, T upper_bound, int l0_sensitivity, int linf_sensitivity) { - return this->build(epsilon, std::nullopt, lower_bound, upper_bound, - l0_sensitivity, linf_sensitivity); + return this->build(epsilon, delta, lower_bound, upper_bound, l0_sensitivity, + linf_sensitivity); }), - py::arg("epsilon"), py::arg("lower_bound"), py::arg("upper_bound"), - py::arg("delta") = 0, py::arg("l0_sensitivity") = 1, + 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, std::nullopt, std::nullopt, std::nullopt, - l0_sensitivity, 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); From edfddd82b85359f3a4783737d9a96e833d7c96fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20S=C3=A1nchez=20Medina?= Date: Sun, 9 Aug 2020 22:17:23 +0100 Subject: [PATCH 4/4] Fix Count result call --- src/bindings/PyDP/pydp_lib/algorithm_builder.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp index 97e20e8f..8c27d294 100644 --- a/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp +++ b/src/bindings/PyDP/pydp_lib/algorithm_builder.hpp @@ -123,7 +123,7 @@ class AlgorithmBuilder { throw std::runtime_error(result.status().error_message()); } - return dp::GetValue(result.ValueOrDie()); + return dp::GetValue(result.ValueOrDie()); }); pyself.def("partial_result", [](Algorithm& pythis) {