From d59291d4bae92692f4dc305cd95fda7167e08cab Mon Sep 17 00:00:00 2001 From: Vadym Doroshenko Date: Fri, 21 Oct 2022 08:02:38 +0200 Subject: [PATCH 1/4] impl --- build_PyDP.sh | 1 + .../PyDP/algorithms/qunatile_tree.cpp | 23 +++++++++++--- tests/algorithms/test_quantile_tree.py | 31 +++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/build_PyDP.sh b/build_PyDP.sh index 56049c28..c60559b1 100755 --- a/build_PyDP.sh +++ b/build_PyDP.sh @@ -13,6 +13,7 @@ echo -e "Running bazel with:\n\tPLATFORM=$PLATFORM\n\tPYTHONHOME=$PYTHONHOME\n\t # Compile code bazel coverage src/python:pydp \ --config $PLATFORM \ +--subcommands \ --verbose_failures \ --action_env=PYTHON_BIN_PATH=$PYTHONHOME \ --action_env=PYTHON_LIB_PATH=$PYTHONPATH diff --git a/src/bindings/PyDP/algorithms/qunatile_tree.cpp b/src/bindings/PyDP/algorithms/qunatile_tree.cpp index 3cdb9dc9..2710536f 100644 --- a/src/bindings/PyDP/algorithms/qunatile_tree.cpp +++ b/src/bindings/PyDP/algorithms/qunatile_tree.cpp @@ -44,7 +44,7 @@ dp::QuantileTree::Privatized GetPrivatizeTree( dp_params.mechanism_builder = std::make_unique(); } else { throw py::value_error("noise_type can be 'laplace' or 'gaussian', but it is '" + - noise_type + "'./**/"); + noise_type + "'."); } auto status_or_result = tree.MakePrivate(dp_params); if (!status_or_result.ok()) { @@ -89,15 +89,28 @@ void init_algorithms_quantile_tree(py::module& m) { to_return.mutable_data()->PackFrom(obj.Serialize()); return to_return; }); - py_class.def("merge", &dp::QuantileTree::Merge, py::arg("summary")); + py_class.def( + "merge", + [](dp::QuantileTree& tree, const dp::Summary& summary) { + if (!summary.has_data()) { + throw std::runtime_error("Cannot merge summary, no data."); + } + + dp::BoundedQuantilesSummary quantiles_summary; + if (!summary.data().UnpackTo(&quantiles_summary)) { + throw std::runtime_error("Fail to upack data"); + } + tree.Merge(quantiles_summary); + }, + py::arg("summary")); py_class.def( "compute_quantiles", [](dp::QuantileTree& tree, double epsilon, double delta, - int max_partitions_contributed_to, int max_contributions_per_partition, + int max_partitions_contributed, int max_contributions_per_partition, const std::vector& quantiles, const std::string& noise_type) { dp::QuantileTree::Privatized privatized_tree = - GetPrivatizeTree(tree, epsilon, delta, max_partitions_contributed_to, + GetPrivatizeTree(tree, epsilon, delta, max_partitions_contributed, max_contributions_per_partition, noise_type); std::vector output; @@ -110,7 +123,7 @@ void init_algorithms_quantile_tree(py::module& m) { } return output; }, - py::arg("epsilon"), py::arg("delta"), py::arg("max_partitions_contributed_to"), + py::arg("epsilon"), py::arg("delta"), py::arg("max_partitions_contributed"), py::arg("max_contributions_per_partition"), py::arg("quantiles"), py::arg("noise_type") = "laplace", "Compute multiple quantiles."); diff --git a/tests/algorithms/test_quantile_tree.py b/tests/algorithms/test_quantile_tree.py index 29d14d2a..b613168c 100644 --- a/tests/algorithms/test_quantile_tree.py +++ b/tests/algorithms/test_quantile_tree.py @@ -1,5 +1,6 @@ import pytest +import pydp._pydp as dp from pydp.algorithms.quantile_tree import QuantileTree @@ -70,3 +71,33 @@ def test_quantiles_and_confidence_intervals(self): <= dp_quantile_ci.upper_bound ) assert dp_quantile_ci.upper_bound - dp_quantile_ci.lower_bound < 0.01 + + def test_serialize_deserialize(self): + lower, upper = 0, 1000 + height, branching_factor = 5, 10 + tree1 = QuantileTree(lower, upper, height, branching_factor) + + # Add elements 0,..1000 to the tree. + for i in range(1001): + tree1.add_entry(i) + + serialized_tree = tree1.serialize().to_bytes() + + # Deserialize + # 1.Create empty tree with the same parameters. + tree2 = QuantileTree(lower, upper, height, branching_factor) + # 2. Merge serialized_tree to tree2. + tree2.merge(dp.bytes_to_summary(serialized_tree)) + + # Check that tree2 computes correct quantiles. For this use high + # epsilon, which means small noise and close to the real quantiles. + eps, delta = 10000, 0 + quantiles_to_compute = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 0.9] + dp_quantiles = tree2.compute_quantiles( + eps, delta, 1, 1, quantiles_to_compute, "laplace" + ) + + # Check that DP quantiles are close to expected. + for quantile, dp_quantile in zip(quantiles_to_compute, dp_quantiles): + expected_quantile = quantile * upper + assert abs(expected_quantile - dp_quantile) < 0.1 From 6fecb0a0b0ae68addaf709ac244301588b5c63d7 Mon Sep 17 00:00:00 2001 From: Vadym Doroshenko Date: Fri, 21 Oct 2022 08:06:41 +0200 Subject: [PATCH 2/4] polishing --- build_PyDP.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/build_PyDP.sh b/build_PyDP.sh index c60559b1..56049c28 100755 --- a/build_PyDP.sh +++ b/build_PyDP.sh @@ -13,7 +13,6 @@ echo -e "Running bazel with:\n\tPLATFORM=$PLATFORM\n\tPYTHONHOME=$PYTHONHOME\n\t # Compile code bazel coverage src/python:pydp \ --config $PLATFORM \ ---subcommands \ --verbose_failures \ --action_env=PYTHON_BIN_PATH=$PYTHONHOME \ --action_env=PYTHON_LIB_PATH=$PYTHONPATH From 2829bf0029719785acf528d85ff3e5cdfc979776 Mon Sep 17 00:00:00 2001 From: Vadym Doroshenko Date: Fri, 21 Oct 2022 10:46:06 +0200 Subject: [PATCH 3/4] More renamings --- src/bindings/PyDP/algorithms/qunatile_tree.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bindings/PyDP/algorithms/qunatile_tree.cpp b/src/bindings/PyDP/algorithms/qunatile_tree.cpp index 2710536f..e18618ad 100644 --- a/src/bindings/PyDP/algorithms/qunatile_tree.cpp +++ b/src/bindings/PyDP/algorithms/qunatile_tree.cpp @@ -30,13 +30,13 @@ std::unique_ptr> CreateQuantileTree(double lower, doubl dp::QuantileTree::Privatized GetPrivatizeTree( dp::QuantileTree& tree, double epsilon, double delta, - int max_partitions_contributed_to, int max_contributions_per_partition, + int max_partitions_contributed, int max_contributions_per_partition, const std::string& noise_type) { dp::QuantileTree::DPParams dp_params; dp_params.epsilon = epsilon; dp_params.delta = delta; dp_params.max_contributions_per_partition = max_contributions_per_partition; - dp_params.max_partitions_contributed_to = max_partitions_contributed_to; + dp_params.max_partitions_contributed = max_partitions_contributed; // Create DP mechanism. if (noise_type == "laplace") { dp_params.mechanism_builder = std::make_unique(); @@ -130,11 +130,11 @@ void init_algorithms_quantile_tree(py::module& m) { py_class.def( "compute_quantiles_and_confidence_intervals", [](dp::QuantileTree& tree, double epsilon, double delta, - int max_contributions_per_partition, int max_partitions_contributed_to, + int max_contributions_per_partition, int max_partitions_contributed, const std::vector& quantiles, double confidence_interval_level, const std::string& noise_type) { dp::QuantileTree::Privatized privatized_tree = - GetPrivatizeTree(tree, epsilon, delta, max_partitions_contributed_to, + GetPrivatizeTree(tree, epsilon, delta, max_partitions_contributed, max_contributions_per_partition, noise_type); std::vector output; @@ -155,7 +155,7 @@ void init_algorithms_quantile_tree(py::module& m) { } return output; }, - py::arg("epsilon"), py::arg("delta"), py::arg("max_partitions_contributed_to"), + py::arg("epsilon"), py::arg("delta"), py::arg("max_partitions_contributed"), py::arg("max_contributions_per_partition"), py::arg("quantiles"), py::arg("confidence_interval_level"), py::arg("noise_type") = "laplace", "Compute multiple quantiles and confidence intervals for them."); From 0750b6764161e3c24ea6ac218cc0297ff40b8931 Mon Sep 17 00:00:00 2001 From: Vadym Doroshenko Date: Fri, 21 Oct 2022 13:10:04 +0200 Subject: [PATCH 4/4] Fix compilation error --- src/bindings/PyDP/algorithms/qunatile_tree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings/PyDP/algorithms/qunatile_tree.cpp b/src/bindings/PyDP/algorithms/qunatile_tree.cpp index e18618ad..93970a81 100644 --- a/src/bindings/PyDP/algorithms/qunatile_tree.cpp +++ b/src/bindings/PyDP/algorithms/qunatile_tree.cpp @@ -36,7 +36,7 @@ dp::QuantileTree::Privatized GetPrivatizeTree( dp_params.epsilon = epsilon; dp_params.delta = delta; dp_params.max_contributions_per_partition = max_contributions_per_partition; - dp_params.max_partitions_contributed = max_partitions_contributed; + dp_params.max_partitions_contributed_to = max_partitions_contributed; // Create DP mechanism. if (noise_type == "laplace") { dp_params.mechanism_builder = std::make_unique();