Skip to content

Commit 44fa4b9

Browse files
authored
Merge pull request #4221 from ilyamironov/master
Addressing reviewers' comments. Added correct handling of order=np.inf.
2 parents 1d76d3a + 2de7692 commit 44fa4b9

File tree

2 files changed

+40
-25
lines changed

2 files changed

+40
-25
lines changed

research/differential_privacy/privacy_accountant/python/rdp_accountant.py

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,34 @@
1616
1717
Functionality for computing Renyi differential privacy of an additive Gaussian
1818
mechanism with sampling. Its public interface consists of two methods:
19-
compute_rdp(q, sigma, T, order) computes RDP with sampling probability q,
20-
noise sigma, T steps at a given order.
21-
get_privacy_spent computes delta (or eps) given RDP and eps (or delta).
19+
compute_rdp(q, sigma, T, orders) computes RDP with the sampling rate q,
20+
noise sigma, T steps at the list of orders.
21+
get_privacy_spent(orders, rdp, target_eps, target_delta) computes delta
22+
(or eps) given RDP at multiple orders and
23+
a target value for eps (or delta).
2224
2325
Example use:
2426
2527
Suppose that we have run an algorithm with parameters, an array of
2628
(q1, sigma1, T1) ... (qk, sigma_k, Tk), and we wish to compute eps for a given
2729
delta. The example code would be:
2830
29-
max_order = 32
30-
orders = range(2, max_order + 1)
31-
rdp = np.zeros_like(orders, dtype=float)
32-
for q, sigma, T in parameters:
33-
rdp += rdp_accountant.compute_rdp(q, sigma, T, orders)
34-
eps, _, opt_order = rdp_accountant.get_privacy_spent(rdp, target_delta=delta)
31+
max_order = 32
32+
orders = range(2, max_order + 1)
33+
rdp = np.zeros_like(orders, dtype=float)
34+
for q, sigma, T in parameters:
35+
rdp += rdp_accountant.compute_rdp(q, sigma, T, orders)
36+
eps, _, opt_order = rdp_accountant.get_privacy_spent(rdp, target_delta=delta)
3537
"""
3638
from __future__ import absolute_import
3739
from __future__ import division
3840
from __future__ import print_function
3941

40-
import math
4142
import sys
4243

4344
from absl import app
4445
from absl import flags
46+
import math
4547
import numpy as np
4648
from scipy import special
4749

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

169171

170172
def _compute_log_a(q, sigma, alpha):
173+
"""Compute log(A_alpha) for any positive finite alpha."""
171174
if float(alpha).is_integer():
172175
return _compute_log_a_int(q, sigma, int(alpha))
173176
else:
@@ -196,7 +199,7 @@ def _compute_zs(sigma, q):
196199

197200

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

317320

318-
def _compute_rdp(q, sigma, steps, alpha):
321+
def _compute_rdp(q, sigma, alpha):
322+
"""Compute RDP of the Gaussian mechanism with sampling at order alpha.
323+
324+
Args:
325+
q: The sampling rate.
326+
sigma: The std of the additive Gaussian noise.
327+
alpha: The order at which RDP is computed.
328+
329+
Returns:
330+
RDP at alpha, can be np.inf.
331+
"""
332+
if np.isinf(alpha):
333+
return np.inf
334+
319335
log_moment_a = _compute_log_a(q, sigma, alpha - 1)
320336

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

338-
return max(log_moment_a, log_bound_b) * steps / (alpha - 1)
354+
return max(log_moment_a, log_bound_b) / (alpha - 1)
339355

340356

341357
def compute_rdp(q, sigma, steps, orders):
342358
"""Compute RDP of Gaussian mechanism with sampling for given parameters.
343359
344360
Args:
345-
q: The sampling ratio.
346-
sigma: The noise sigma.
361+
q: The sampling rate.
362+
sigma: The std of the additive Gaussian noise.
347363
steps: The number of steps.
348364
orders: An array (or a scalar) of RDP orders.
349365
@@ -352,20 +368,19 @@ def compute_rdp(q, sigma, steps, orders):
352368
"""
353369

354370
if np.isscalar(orders):
355-
return _compute_rdp(q, sigma, steps, orders)
371+
rdp = _compute_rdp(q, sigma, orders)
356372
else:
357-
rdp = np.zeros_like(orders, dtype=float)
358-
for i, order in enumerate(orders):
359-
rdp[i] = _compute_rdp(q, sigma, steps, order)
360-
return rdp
373+
rdp = np.array([_compute_rdp(q, sigma, order) for order in orders])
374+
375+
return rdp * steps
361376

362377

363378
def get_privacy_spent(orders, rdp, target_eps=None, target_delta=None):
364379
"""Compute delta (or eps) for given eps (or delta) from the RDP curve.
365380
366381
Args:
367382
orders: An array (or a scalar) of RDP orders.
368-
rdp: An array of RDP values.
383+
rdp: An array of RDP values. Must be of the same length as the orders list.
369384
target_eps: If not None, the epsilon for which we compute the corresponding
370385
delta.
371386
target_delta: If not None, the delta for which we compute the corresponding

research/differential_privacy/privacy_accountant/python/rdp_accountant_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2-
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
32
#
43
# Licensed under the Apache License, Version 2.0 (the "License");
54
# you may not use this file except in compliance with the License.
@@ -95,7 +94,7 @@ def _compute_rdp_mp(self, q, sigma, order):
9594
return log_a_mp, log_b_mp
9695

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

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

126-
rdp_vec = rdp_accountant.compute_rdp(0.01, 2.5, 50, [1.5, 2.5, 5, 50, 100])
125+
rdp_vec = rdp_accountant.compute_rdp(0.01, 2.5, 50, [1.5, 2.5, 5, 50, 100,
126+
np.inf])
127127

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

0 commit comments

Comments
 (0)