def test_get_epsilon_R_exceptions(self): """ Tests that get_epsilon_R raises errors when encountering instabilities.""" nc = 10 ks = 10 * np.ones( nc, dtype=np.int32 ) #number of compositions for each value of (q,sigma) with self.assertRaises(ValueError): get_epsilon_R(sigma=np.linspace(0.0005, 0.0015, nc), q=np.linspace(0.15, 0.25, nc), ncomp=ks, target_delta=1e-4, nx=1E6, L=40.0)
def approximate_sigma_remove_relation( target_eps: float, delta: float, q: float, num_iter: int, tol: Optional[float] = 1e-4, force_smaller: Optional[bool] = False, maxeval: Optional[int] = 10) -> Tuple[float, float, int]: """ Approximates the sigma corresponding to a target privacy epsilon using the Fourier Accountant for the add/remove relation. Uses a bracketing approach where an initial rough estimate of lower and upper bounds for sigma is iteratively shrunk. Each iteration fits a logarithmic function eps->sigma to the bounds, which is evaluated at target_eps to get the next estimate for sigma, which is in turn used to update the bounds. :param compute_eps_fn: Privacy accountant function returning epsilon for a given sigma, assumed to be monotonic decreasing. :param target_eps: The desired target epsilon. :param delta: The delta privacy parameter. :param q: The subsampling ratio. :param num_iter: The number of batch iterations. :param tol: Absolute tolerance for the epsilon corresponding to the returned value for sigma, i.e., `abs(compute_eps_fn(sigma_opt) - target_eps) < tol` :param force_smaller: Require that the returned value for sigma results in an epsilon that is strictly smaller than `target_eps`. This may slightly violate `tol`. :param maxeval: Maximum number of evaluations of `compute_eps_fn`. The function aborts the search for sigma after `maxeval` function evaluations were made and returns the current best estimate. In that case, `tol` is violated but `force_smaller` is still adhered to. :return: Tuple consisting of 1) the determined value for sigma 2) the corresponding espilon 3) the number of function evaluations made """ L = max(20, target_eps * 2) compute_eps = lambda sigma, precision=1: get_epsilon_R( delta, sigma, q, ncomp=num_iter, L=L * precision, nx=1e6 * (L * precision) / 20) return _approximate_sigma(compute_eps, target_eps, q, tol, force_smaller, maxeval)
def test_get_epsilon_R_regression_valid_params(self): """ Tests that results of get_epsilon_R did not drift from last version.""" nc = 10 sigmas = np.linspace(1.6, 1.8, nc) q_values = np.linspace(0.05, 0.06, nc) ks = 10 * np.ones( nc, dtype=np.int32 ) #number of compositions for each value of (q,sigma) test_data = [ (dict(sigma=sigmas, q=q_values, ncomp=ks, target_delta=1e-3, nx=1E6, L=20.0), 0.9658229984720059), (dict(sigma=sigmas, q=q_values, ncomp=ks, target_delta=1e-3, nx=1E6, L=40.0), 0.9658230049986837), (dict(sigma=sigmas, q=q_values, ncomp=ks, target_delta=1e-3, nx=1E5, L=20.0), 0.9658230533746907), (dict(sigma=sigmas, q=q_values, ncomp=ks, target_delta=1e-4, nx=1E6, L=20.0), 1.2658081375076573), (dict(sigma=sigmas[:-1], q=q_values[:-1], ncomp=ks[:-1], target_delta=1e-3, nx=1E6, L=20.0), 0.9092052395489234), ] for params, expected in test_data: actual = get_epsilon_R(**params) self.assertAlmostEqual(expected, actual)
def test_get_epsilon_R_invalid_sizes(self): with self.assertRaises(ValueError): get_epsilon_R(sigma=np.ones(2), q=np.ones(2), ncomp=np.ones(1, dtype=np.int32)) with self.assertRaises(ValueError): get_epsilon_R(sigma=np.ones(2), q=np.ones(1), ncomp=np.ones(2, dtype=np.int32)) with self.assertRaises(ValueError): get_epsilon_R(sigma=np.ones(1), q=np.ones(2), ncomp=np.ones(2, dtype=np.int32))
#Compute delta in substitute relation d2 = compute_delta.get_delta_S(q=q, sigma=sigma, target_eps=eps, L=L, nx=nx, ncomp=nc) # Examples for computing epsilon as a function of delta delta = 1e-7 #Compute epsilon in remove/add relation e1 = compute_eps.get_epsilon_R(q=q, sigma=sigma, target_delta=delta, L=L, nx=nx, ncomp=nc) #Compute epsilon in substitute relation e2 = compute_eps.get_epsilon_S(q=q, sigma=sigma, target_delta=delta, L=L, nx=nx, ncomp=nc) # Examples for varying sigma and q L = 20 nx = 2E6
def get_epsilon(self, target_delta, q, num_epochs=None, num_iter=None): num_iter = self._validate_epochs_and_iter(num_epochs, num_iter, q) eps = get_epsilon_R(target_delta, self._dp_scale, q, ncomp=num_iter) return eps