Skip to content

Commit

Permalink
Merge remote-tracking branch 'yuxiangw/master' into gsvt_c1_delta
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Lebensold committed Jul 7, 2023
2 parents 972dc84 + 58b9854 commit a19d8cc
Show file tree
Hide file tree
Showing 38 changed files with 2,222 additions and 2,808 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]
python-version: [3.7, 3.8, 3.9]

steps:
# Checks-out the repository under $GITHUB_WORKSPACE, which is the CWD for
Expand Down
3 changes: 1 addition & 2 deletions autodp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from autodp.rdp_acct import anaRDPacct
from autodp.dp_acct import DP_acct

131 changes: 74 additions & 57 deletions autodp/autodp_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
"""

import numpy as np
from autodp import converter, cdf_bank
from scipy.optimize import minimize_scalar, root_scalar
from autodp import converter

class Mechanism():
"""
Expand Down Expand Up @@ -60,10 +59,18 @@ def approxRDP(delta, alpha):
def approxDP(delta, estimated_eps=None):
return np.inf

def cdf_p(x):
return 0
def cdf_q(x):
return 0
def cdf_p2q(x):
return None
def cdf_q2p(x):
return None
def pdf_p(x):
return None
def pdf_q(x):
return None
def log_phi_p2q(x):
return None
def log_phi_q2p(x):
return None

def approx_delta(eps):
return 1
Expand All @@ -77,9 +84,15 @@ def fDP(fpr):
self.approxDP = approxDP
self.approx_delta =approx_delta
self.fDP = fDP
self.cdf_p = cdf_p
self.cdf_q = cdf_q
self.exact_phi = False # whether admit a closed-form phi-function
#TODO need a dedicated Flag to indicate whether the dominating pair description is available for this mechanism;
# and which mode we are using.
self.cdf_p2q = cdf_p2q # cdf of the privacy loss random variable log(p/q), where p, q denotes the pdf of dominating pair.
self.cdf_q2p = cdf_q2p
self.pdf_p = pdf_p # pdf of the dominating pair.
self.pdf_q = pdf_q
self.log_phi_p2q = log_phi_p2q # log phi function of the privacy loss distribution (log (pdf_p)/(pdf_q))
self.log_phi_q2p = log_phi_q2p
self.exact_phi = True # whether admit a closed-form phi-function
self.eps_pureDP = np.inf # equivalent to RenyiDP(np.inf) and approxDP(0).

self.delta0 = np.inf # indicate the smallest allowable \delta0 in approxRDP that is not inf
Expand Down Expand Up @@ -115,12 +128,12 @@ def get_fDP(self, fpr):
# Output false negative rate as a function of false positive rate
return self.fDP(fpr)

def get_cdf_p(self, x):
def get_cdf_p2q(self, x):
# Output cdf as a function of log(p/q)
return self.cdf_p(x)
def get_cdf_q(self, x):
return self.cdf_p2q(x)
def get_cdf_q2p(self, x):
# Output cdf as a function of log(q/p)
return self.cdf_q(x)
return self.cdf_q2p(x)
def get_pureDP(self):
return self.eps_pureDP

Expand Down Expand Up @@ -161,7 +174,7 @@ def propagate_updates(self, func, type_of_update,
eps = func[0]
delta = func[1]

self.approxRDP = converter.pointwise_minimum_two_args(self.approxRDP,
self.approxRDP = converter.pointwise_minimum_two_arguments(self.approxRDP,
converter.approxdp_to_approxrdp(eps, delta))

def approx_dp_func(delta1):
Expand Down Expand Up @@ -278,75 +291,79 @@ def approx_dp_func(delta1):
self.pdf_p = func[0]
self.pdf_q = func[1]

# convert pdf to phi-function using gaussian quadrature
# TODO: if currant mechanism admits a closed-form phi, then this step is unnecessary.
# Convert pdf to phi-function using gaussian quadrature. if currant mechanism admits a closed-form phi,
# then this step is unnecessary.
# WARNING: this conversion can be numerically unstable, thus, we set exact_phi to be True as a default setting.
if self.exact_phi == False:
def log_phi_p(t): return converter.pdf_to_phi(self.pdf_p, self.pdf_q, t)
def log_phi_q(t): return converter.pdf_to_phi(self.pdf_q, self.pdf_p, t)
def log_phi_p2q(t): return converter.pdf_to_phi(self.pdf_p, self.pdf_q, t)
def log_phi_q2p(t): return converter.pdf_to_phi(self.pdf_q, self.pdf_p, t)
#log_phi_p, log_phi_q = converter.pdf_to_phi(self.pdf_p, self.pdf_q)
self.log_phi_p = log_phi_p
self.log_phi_q = log_phi_q
self.log_phi_p2q = log_phi_p2q
self.log_phi_q2p = log_phi_q2p
# Apply Gaussian quadrature to do numerical inversion.
cdf_p = lambda x: cdf_bank.cdf_quad(log_phi_p, x, n_quad=n_quad)
cdf_q = lambda x: cdf_bank.cdf_quad(log_phi_q, x, n_quad=n_quad)
# cdf_p2q denotes the cdf of privacy loss random variable log(p/q)
# cdf_q2p denotes the cdf of log(q/p)
cdf_p2q = lambda x: converter.phi_to_cdf(log_phi_p2q, x, n_quad=n_quad)
cdf_q2p = lambda x: converter.phi_to_cdf(log_phi_q2p, x, n_quad=n_quad)

self.approxDP = converter.pointwise_minimum(self.approxDP,
converter.cdf_to_approxdp(cdf_p, cdf_q))
converter.cdf_to_approxdp(cdf_p2q, cdf_q2p))
self.approx_delta = converter.pointwise_minimum(self.approx_delta,
converter.cdf_to_approxdelta(cdf_p, cdf_q))
converter.cdf_to_approxdelta(cdf_p2q, cdf_q2p))

elif type_of_update == 'log_phi':
# Analytical Fourier accountant, function will be a pair
# of log characteristic functions.
# cdf_p is for log(p/q) and cdf_q is for log(q/p)
log_phi_p = func[0]
log_phi_q = func[1]
#self.exact_phi = True
# Update Analytical Fourier accountant with a new pair of log characteristic functions.

log_phi_p2q = func[0]
log_phi_q2p = func[1]
self.exact_phi = True
self.log_phi_p2q = log_phi_p2q
self.log_phi_q2p = log_phi_q2p
# Apply Gaussian quadrature to do numerical inversion.
cdf_p = lambda x: cdf_bank.cdf_quad(log_phi_p, x, n_quad = n_quad)
cdf_q = lambda x: cdf_bank.cdf_quad(log_phi_q, x, n_quad = n_quad)
# l is the range of eps
#cdf_p = lambda l: cdf_bank.cdf_approx_fft(log_phi_p, l)
#cdf_q = lambda l: cdf_bank.cdf_approx_fft(log_phi_q, l)
# cdf_p2q is the cdf of log(p/q), and cdf_q2p is the cdf for log(q/p).
cdf_p2q = lambda x: converter.phi_to_cdf(log_phi_p2q, x, n_quad = n_quad)
cdf_q2p = lambda x: converter.phi_to_cdf(log_phi_q2p, x, n_quad = n_quad)

# Other approaches to convert phi function to cdf functions (not recommended)
#cdf_p2q = lambda l: converter.cdf_approx_fft(log_phi_p, l)
#cdf_q2p = lambda l: converter.cdf_approx_fft(log_phi_q, l)

self.approxDP = converter.pointwise_minimum(self.approxDP,
converter.cdf_to_approxdp(cdf_p,cdf_q))
converter.cdf_to_approxdp(cdf_p2q,cdf_q2p))
self.approx_delta = converter.pointwise_minimum(self.approx_delta,
converter.cdf_to_approxdelta(cdf_p, cdf_q))
converter.cdf_to_approxdelta(cdf_p2q, cdf_q2p))
elif type_of_update == 'cdf':
# Analytical CDF: cdf_p is for log(p/q), cdf_q is for log(q/p).
cdf_p = func[0]
cdf_q = func[1]

# Analytical CDF: cdf_p2q is for log(p/q), cdf_q2p is for log(q/p).
cdf_p2q = func[0]
cdf_q2p = func[1]
# propagate to ApproxDP
self.approxDP = converter.pointwise_minimum(self.approxDP,
converter.cdf_to_approxdp(cdf_p, cdf_q))
converter.cdf_to_approxdp(cdf_p2q, cdf_q2p))
# propagate to CDF
self.cdf_p = cdf_p
self.cdf_q = cdf_q

#propagate to discrete phi-function

#propagate to analytical phi-function using quadrature rules
self.phi_p = converter.cdf_phi_p(cdf_p)
self.phi_q = converter.cdf_phi_q(cdf_q)
self.cdf_p2q = cdf_p2q
self.cdf_q2p = cdf_q2p

# TODO: convert CDF to discrete phi functions (FFT based approaches).
"""
self.log_phi_p2q = converter.cdf_phi_p(cdf_p2q)
self.log_phi_q2p = converter.cdf_phi_q(cdf_q2p)
"""
# propagate to ApproxDelta
self.approx_delta = converter.pointwise_minimum(self.approx_delta,
converter.cdf_to_approxdelta(cdf_p, cdf_q))
converter.cdf_to_approxdelta(cdf_p2q, cdf_q2p))

elif type_of_update =='log_phi_adv':
# Todo: Future work when the phi-function is parametrized by
# more than one parameter.
"""
log_phi_p = func[0]
log_phi_q = func[1]
cdf_p = lambda x, t: cdf_bank.cdf_approx(log_phi_p, x, tbd=t)
cdf_q = lambda x, t: cdf_bank.cdf_approx(log_phi_q, x, tbd=t)
log_phi_p2q = func[0]
log_phi_q2p = func[1]
cdf_p2q = lambda x, t: converter.cdf_approx(log_phi_p2q, x, tbd=t)
cdf_q2p = lambda x, t: converter.cdf_approx(log_phi_q2p, x, tbd=t)
# first find t that optimize for one particular delta
# we can set delta = 1e-5, and try out the tbd
normal_equation = lambda tbd: converter.cdf_to_approxdp_tbd(cdf_p, cdf_q, tbd)
normal_equation = lambda tbd: converter.cdf_to_approxdp_tbd(cdf_p2q, cdf_q2p, tbd)
n = 20
clip = (self.tbd_range[1] - self.tbd_range[0])*1.0/n
tbd_list = [self.tbd_range[0] + t*clip for t in range(n)]
Expand All @@ -355,7 +372,7 @@ def log_phi_q(t): return converter.pdf_to_phi(self.pdf_q, self.pdf_p, t)
result_list = np.array(result_list)
t = tbd_list[np.argmax(result_list)]
self.approxDP = converter.cdf_to_approxdp_adv(cdf_p, cdf_q,t)
self.approxDP = converter.cdf_to_approxdp_adv(cdf_p2q, cdf_q2p,t)
#self.approxDP = converter.pointwise_minimum(self.approxDP, converter.cdf_to_approxdp_tbd(func,t))
#self.approx_delta = converter.pointwise_minimum(self.approx_delta,
Expand Down
121 changes: 0 additions & 121 deletions autodp/cdf_bank.py

This file was deleted.

Loading

0 comments on commit a19d8cc

Please sign in to comment.