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
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,34 @@

Functionality for computing Renyi differential privacy of an additive Gaussian
mechanism with sampling. Its public interface consists of two methods:
compute_rdp(q, sigma, T, order) computes RDP with sampling probability q,
noise sigma, T steps at a given order.
get_privacy_spent computes delta (or eps) given RDP and eps (or delta).
compute_rdp(q, sigma, T, orders) computes RDP with the sampling rate q,
noise sigma, T steps at the list of orders.
get_privacy_spent(orders, rdp, target_eps, target_delta) computes delta
(or eps) given RDP at multiple orders and
a target value for eps (or delta).

Example use:

Suppose that we have run an algorithm with parameters, an array of
(q1, sigma1, T1) ... (qk, sigma_k, Tk), and we wish to compute eps for a given
delta. The example code would be:

max_order = 32
orders = range(2, max_order + 1)
rdp = np.zeros_like(orders, dtype=float)
for q, sigma, T in parameters:
rdp += rdp_accountant.compute_rdp(q, sigma, T, orders)
eps, _, opt_order = rdp_accountant.get_privacy_spent(rdp, target_delta=delta)
max_order = 32
orders = range(2, max_order + 1)
rdp = np.zeros_like(orders, dtype=float)
for q, sigma, T in parameters:
rdp += rdp_accountant.compute_rdp(q, sigma, T, orders)
eps, _, opt_order = rdp_accountant.get_privacy_spent(rdp, target_delta=delta)
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import math
import sys

from absl import app
from absl import flags
import math
import numpy as np
from scipy import special

Expand Down Expand Up @@ -168,6 +170,7 @@ def _compute_log_a_frac(q, sigma, alpha):


def _compute_log_a(q, sigma, alpha):
"""Compute log(A_alpha) for any positive finite alpha."""
if float(alpha).is_integer():
return _compute_log_a_int(q, sigma, int(alpha))
else:
Expand Down Expand Up @@ -196,7 +199,7 @@ def _compute_zs(sigma, q):


def _compute_log_b0(sigma, q, alpha, z1):
"""Return an approximation to B0 or None if failed to converge."""
"""Return an approximation to log(B0) or None if failed to converge."""
z0, _ = _compute_zs(sigma, q)
s, log_term, log_b0, k, sign, max_log_term = 0, 1., 0, 0, 1, -np.inf
# Keep adding new terms until precision is no longer preserved.
Expand Down Expand Up @@ -315,7 +318,20 @@ def _compute_eps(orders, rdp, delta):
return eps[idx_opt], orders_vec[idx_opt]


def _compute_rdp(q, sigma, steps, alpha):
def _compute_rdp(q, sigma, alpha):
"""Compute RDP of the Gaussian mechanism with sampling at order alpha.

Args:
q: The sampling rate.
sigma: The std of the additive Gaussian noise.
alpha: The order at which RDP is computed.

Returns:
RDP at alpha, can be np.inf.
"""
if np.isinf(alpha):
return np.inf

log_moment_a = _compute_log_a(q, sigma, alpha - 1)

log_bound_b = _log_bound_b_elementary(q, alpha - 1) # does not require sigma
Expand All @@ -335,15 +351,15 @@ def _compute_rdp(q, sigma, steps, alpha):
_log_print(log_bound_b2), _log_print(log_bound_b)))
log_bound_b = min(log_bound_b, log_bound_b2)

return max(log_moment_a, log_bound_b) * steps / (alpha - 1)
return max(log_moment_a, log_bound_b) / (alpha - 1)


def compute_rdp(q, sigma, steps, orders):
"""Compute RDP of Gaussian mechanism with sampling for given parameters.

Args:
q: The sampling ratio.
sigma: The noise sigma.
q: The sampling rate.
sigma: The std of the additive Gaussian noise.
steps: The number of steps.
orders: An array (or a scalar) of RDP orders.

Expand All @@ -352,20 +368,19 @@ def compute_rdp(q, sigma, steps, orders):
"""

if np.isscalar(orders):
return _compute_rdp(q, sigma, steps, orders)
rdp = _compute_rdp(q, sigma, orders)
else:
rdp = np.zeros_like(orders, dtype=float)
for i, order in enumerate(orders):
rdp[i] = _compute_rdp(q, sigma, steps, order)
return rdp
rdp = np.array([_compute_rdp(q, sigma, order) for order in orders])

return rdp * steps


def get_privacy_spent(orders, rdp, target_eps=None, target_delta=None):
"""Compute delta (or eps) for given eps (or delta) from the RDP curve.

Args:
orders: An array (or a scalar) of RDP orders.
rdp: An array of RDP values.
rdp: An array of RDP values. Must be of the same length as the orders list.
target_eps: If not None, the epsilon for which we compute the corresponding
delta.
target_delta: If not None, the delta for which we compute the corresponding
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -95,7 +94,7 @@ def _compute_rdp_mp(self, q, sigma, order):
return log_a_mp, log_b_mp

# TEST ROUTINES
def _almost_equal(self, a, b, rtol, atol=0):
def _almost_equal(self, a, b, rtol, atol=0.):
# Analogue of np.testing.assert_allclose(a, b, rtol, atol).
self.assertBetween(a, b * (1 - rtol) - atol, b * (1 + rtol) + atol)

Expand Down Expand Up @@ -123,9 +122,10 @@ def test_compute_rdp(self):
rdp_scalar = rdp_accountant.compute_rdp(0.1, 2, 10, 5)
self.assertAlmostEqual(rdp_scalar, 0.07737, places=5)

rdp_vec = rdp_accountant.compute_rdp(0.01, 2.5, 50, [1.5, 2.5, 5, 50, 100])
rdp_vec = rdp_accountant.compute_rdp(0.01, 2.5, 50, [1.5, 2.5, 5, 50, 100,
np.inf])

correct = [0.00065, 0.001085, 0.00218075, 0.023846, 167.416307]
correct = [0.00065, 0.001085, 0.00218075, 0.023846, 167.416307, np.inf]
for i in range(len(rdp_vec)):
self.assertAlmostEqual(rdp_vec[i], correct[i], places=5)

Expand Down