Source code for secretflow.security.privacy.accounting.rdp_accountant

# Copyright 2022 Ant Group Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import math
import numpy as np
from .log_utils import log_alpha

"""Implements privacy accounting for Rényi Differential Privacy.
"""


[docs]def rdp_core(q: float, noise_multiplier: float, alpha: float): """Comnpute RDP of the Sampled Gaussian mechanism at order alpha. Args: q: The sampling rate. noise_multiplier: The noise_multiplier used for calculating the std of the additive Gaussian noise. alpha: The order at which RDP is cald Returns: RDP at alpha, can be np.inf. """ if q == 0: return 0 if q == 1: return alpha / (2 * noise_multiplier**2) if np.isinf(alpha): return np.inf return log_alpha(q, noise_multiplier, alpha) / (alpha - 1)
[docs]def get_rdp(q: float, noise_multiplier: float, steps: int, orders): """Calculate RDP of the Sampled Gaussian Mechanism. Args: q: The sampling rate. noise_multiplier: The ratio of the standard deviation of the Gaussian noise to the l2-sensitivity of the function to which it is added steps: The number of steps. orders: An array (or a scalar) of RDP orders. Returns: The RDPs at all orders. Can be `np.inf`. """ if np.isscalar(orders): rdp = rdp_core(q, noise_multiplier, orders) else: rdp = np.array([rdp_core(q, noise_multiplier, order) for order in orders]) return rdp * steps
[docs]def cal_delta(orders, rdp, eps: float): """Calculate delta given a list of RDP values and target epsilon. Args: orders: An array (or a scalar) of orders. rdp: A list (or a scalar) of RDP guarantees. eps: The target epsilon. Returns: Pair of (delta, optimal_order). Raises: ValueError: If input is malformed. """ orders_vec = np.atleast_1d(orders) rdp_vec = np.atleast_1d(rdp) if eps < 0: raise ValueError("Value of privacy loss bound epsilon must be >=0.") if len(orders_vec) != len(rdp_vec): raise ValueError("Input lists must have the same length.") logdeltas = [] for (a, r) in zip(orders_vec, rdp_vec): if a < 1: raise ValueError("Renyi divergence order must be >=1.") if r < 0: raise ValueError("Renyi divergence must be >=0.") logdelta = 0.5 * math.log1p(-math.exp(-r)) if a > 1.01: rdp_bound = (a - 1) * (r - eps + math.log1p(-1 / a)) - math.log(a) logdelta = min(logdelta, rdp_bound) logdeltas.append(logdelta) idx_opt = np.argmin(logdeltas) return min(math.exp(logdeltas[idx_opt]), 1.0), orders_vec[idx_opt]
[docs]def cal_eps(orders, rdp, delta: float): """Calculate epsilon given a list of RDP values and target delta. Args: orders: An array (or a scalar) of orders. rdp: A list (or a scalar) of RDP guarantees. delta: The target delta. Returns: Pair of (eps, optimal_order). Raises: ValueError: If input is malformed. """ orders_vec = np.atleast_1d(orders) rdp_vec = np.atleast_1d(rdp) if delta <= 0: raise ValueError("Privacy failure probability bound delta must be >0.") if len(orders_vec) != len(rdp_vec): raise ValueError("Input lists must have the same length.") eps_vec = [] for (a, r) in zip(orders_vec, rdp_vec): if a < 1: raise ValueError("Renyi divergence order must be >=1.") if r < 0: raise ValueError("Renyi divergence must be >=0.") if delta**2 + math.expm1(-r) >= 0: eps = 0 elif a > 1.01: eps = r + math.log1p(-1 / a) - math.log(delta * a) / (a - 1) else: eps = np.inf eps_vec.append(eps) idx_opt = np.argmin(eps_vec) return max(0, eps_vec[idx_opt]), orders_vec[idx_opt]
[docs]def get_privacy_spent_rdp( orders, rdp, target_eps: float = None, target_delta: float = None ): """Calculates delta (or eps) for given eps (or delta) from RDP values. Args: orders: An array (or a scalar) of RDP orders. 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 cal the corresponding delta. target_delta: If not `None`, the delta for which we cal the corresponding epsilon. Exactly one of `target_eps` and `target_delta` must be `None`. Returns: A tuple of epsilon, delta, and the optimal order. Raises: ValueError: If target_eps and target_delta are messed up. """ if target_eps is None and target_delta is None: raise ValueError("Exactly one out of eps and delta must be None. (Both are).") if target_eps is not None and target_delta is not None: raise ValueError("Exactly one out of eps and delta must be None. (None is).") if target_eps is not None: delta, opt_order = cal_delta(orders, rdp, target_eps) return target_eps, delta, opt_order else: eps, opt_order = cal_eps(orders, rdp, target_delta) return eps, target_delta, opt_order