Пример #1
0
def limited_information_lower_bound_IL(P0: np.ndarray, P1: np.ndarray,
                                       pi0: np.ndarray, pi1: np.ndarray):
    """ Computes E_x[sup_y d(P1^{pi1}(y'|x), P0^{pi0}(y'|x))], which
        lower bounds I_L
    Parameters
    ----------
    P0, P1 : np.ndarray
        Numpy matrices containing the transition probabilities for models M0 and M1
        Each matrix should have dimensions |actions|x|states|x|states|
    pi0, pi1 : np.ndarray
        Numpy matrix of dimensions |states|x|actions| containing the
        policies probabilities
    Returns
    -------
    L : float
        Lower bound of I_L
    """
    P0, P1 = sanity_check_probabilities(P0, P1)
    na = P0.shape[0]
    ns = P1.shape[1]
    P1_p1 = build_markov_transition_density(P1, pi1)
    P0_p0 = build_markov_transition_density(P0, pi0)
    _, mu1 = compute_stationary_distribution(P1, pi1)

    d = sp.special.kl_div(P1_p1, P0_p0) + sp.special.kl_div(
        1 - P1_p1, 1 - P0_p0)

    return np.dot(mu1, np.max(d, axis=0))
Пример #2
0
def full_information_privacy(P0: np.ndarray, P1: np.ndarray, xi0: np.ndarray,
                             xi1: np.ndarray) -> float:
    """ Computes 1/I_F(pi_0, pi_1) given xi_0 and xi_1
    Parameters
    ----------
    P0, P1 : np.ndarray
        Numpy matrices containing the transition probabilities for model M0 and M1
        Each matrix should have dimensions |actions|x|states|x|states|
    xi0, xi1 : np.ndarray
        Numpy matrices of dimensions |states|x|actions| containing the stationary distributions
        over states and actions of the two models (M0 and M1)

    Returns
    -------
    1/I_F : float
        Privacy level
    """
    P0, P1 = sanity_check_probabilities(P0, P1)
    xi0, xi1 = np.array(xi0), np.array(xi1)
    na, ns = P0.shape[0], P0.shape[1]

    I = compute_KL_divergence_models(P0, P1)

    privacy = np.sum(np.multiply(xi1, I))
    for s in range(ns):
        mu1_s = np.sum(
            xi1[s, :]) if not np.isclose(np.sum(xi1[s, :]), 0) else 1.
        mu0_s = np.sum(
            xi0[s, :]) if not np.isclose(np.sum(xi0[s, :]), 0) else 1.
        pi1_s = xi1[s, :] / mu1_s
        pi0_s = xi0[s, :] / mu0_s
        privacy += mu1_s * np.sum(sp.special.rel_entr(pi1_s, pi0_s))

    return 1 / privacy if not np.isclose(privacy, 0.) else np.infty
Пример #3
0
def full_information_privacy_lb(P0: np.ndarray,
                                P1: np.ndarray,
                                solver=cp.ECOS,
                                debug=False):
    """ Computes the policy that achieves the best level of privacy in the
    full information setting
    Parameters
    ----------
    P0, P1 : np.ndarray
        Numpy matrices containing the transition probabilities for model M0 and M1
        Each matrix should have dimensions |actions|x|states|x|states|
    solver : cvxpy.Solver, optional
        Solver used to solve the problem. Default solver is ECOS
    debug : bool, optional
        If true, prints the solver output. Default value is False
    Returns
    -------
    I_F : float
        Inverse of the privacy level
    xi : np.ndarray
        Stationary distribution over states and actions achieving the best
        level of privacy
    """

    # Check the matrices are ok
    P0, P1 = sanity_check_probabilities(P0, P1)
    na, ns = P0.shape[0], P0.shape[1]

    # Compute KL divergences
    I = compute_KL_divergence_models(P0, P1)

    best_res, best_xi = np.inf, None

    # Construct the problem to find minimum privacy
    xi = cp.Variable((ns, na), nonneg=True)

    objective = cp.Minimize(cp.sum(cp.multiply(xi, I)))

    # stationarity_constraint
    stationarity_constraint = 0
    for a in range(na):
        stationarity_constraint += xi[:, a].T @ (P1[a, :, :] - np.eye(ns))

    constraints = [stationarity_constraint == 0, cp.sum(xi) == 1]

    # Solve problem
    problem = cp.Problem(objective, constraints)
    try:
        result = problem.solve(verbose=debug, solver=solver)
    except Exception as err:
        raise Exception('Problem failed!')

    # Check if results are better than previous ones
    best_res, best_xi = result, xi.value

    # Make sure to normalize the results
    best_xi += eps
    best_xi /= np.sum(best_xi) if not np.isclose(np.sum(best_xi), 0) else 1.
    return best_res, best_xi
Пример #4
0
def limited_information_privacy(P0: np.ndarray, P1: np.ndarray,
                                xi0: np.ndarray, xi1: np.ndarray) -> float:
    """ Computes 1/I_L(pi_0, pi_1) given xi_0 and xi_1
    Parameters
    ----------
    P0, P1 : np.ndarray
        Numpy matrices containing the transition probabilities for model M0 and M1
        Each matrix should have dimensions |actions|x|states|x|states|
    xi0, xi1 : np.ndarray
        Numpy matrices of dimensions |states|x|actions| containing the stationary distributions
        over states and actions of the two models (M0 and M1)

    Returns
    -------
    1/I_L : float
        Privacy level
    """

    P0, P1 = sanity_check_probabilities(P0, P1)
    xi0, xi1 = np.array(xi0), np.array(xi1)
    na, ns = P0.shape[0], P0.shape[1]

    privacy = 0
    for s in range(ns):
        z = sp.special.rel_entr(np.sum(xi1[s, :]), np.sum(xi0[s, :]))
        if z == np.infty:
            print(
                'An infinity was computed in a KL-Divergence. Check the first term: {}'
                .format(np.sum(mu1[s, :])))
            z = 0
        privacy -= z

        for y in range(ns):
            z = sp.special.rel_entr(xi1[s, :] @ P1[:, s, y],
                                    xi0[s, :] @ P0[:, s, y])
            if z == np.infty:
                print(
                    'An infinity was computed in a KL-Divergence. Check the first term: {}'
                    .format(xi1[s, :] @ P1[:, s, y]))
                z = 0
            privacy += z

    return 1 / privacy if not np.isclose(privacy, 0.) else np.infty
Пример #5
0
def limited_information_privacy_approximate_upper_lb(P0: np.ndarray,
                                                     P1: np.ndarray):
    """ Computes a pair of policies that upper bounds the privacy lower bound
    Parameters
    ----------
    P0, P1 : np.ndarray
        Numpy matrices containing the transition probabilities for models M0 and M1
        Each matrix should have dimensions |actions|x|states|x|states|
    Returns
    -------
    L : float
        Upper bound of I_L
    pi0, pi1 : np.ndarray
        The computed policies
    """
    P0, P1 = sanity_check_probabilities(P0, P1)
    na = P0.shape[0]
    ns = P1.shape[1]
    gamma = cp.Variable(1, nonneg=True)
    pi0 = cp.Variable((ns, na), nonneg=True)
    pi1 = cp.Variable((ns, na), nonneg=True)

    constraint = []
    constraint_pi0 = [cp.sum(pi0[s, :]) == 1 for s in range(ns)]
    constraint_pi1 = [cp.sum(pi1[s, :]) == 1 for s in range(ns)]
    for s in range(ns):
        Ds = 0.
        for y in range(ns):
            P1_pi1 = P1[:, s, y] @ pi1[s, :]
            P0_pi0 = P0[:, s, y] @ pi0[s, :]
            Ds += cp.kl_div(P1_pi1, P0_pi0) + P1_pi1 - P0_pi0
        constraint += [Ds <= gamma]

    constraints = constraint + constraint_pi0 + constraint_pi1
    problem = cp.Problem(cp.Minimize(gamma), constraints)

    result = problem.solve()
    return result, pi0.value, pi1.value
Пример #6
0
def limited_information_privacy_utility(rho: float,
                                        lmbd: float,
                                        P0: np.ndarray,
                                        P1: np.ndarray,
                                        R0: np.ndarray,
                                        R1: np.ndarray,
                                        initial_points: int = 1,
                                        max_iterations: int = 30,
                                        solver=cp.ECOS,
                                        debug: bool = False,
                                        pi0: np.ndarray = None):
    """ Optimize the privacy-utility value function over the two policies
    in the limited information setting
    Parameters
    ----------
    rho : float
        Weight given to policy pi_1 (1-rho for policy pi_0)
    lmbd : float
        Weight given to the privacy term
    P0, P1 : np.ndarray
        Numpy matrices containing the transition probabilities for model M0 and M1
        Each matrix should have dimensions |actions|x|states|x|states|
    R0, R1 : np.ndarray
        Numpy matrices containing the rewards for model M0 and M1
        Each matrix should have dimensions |states|x|actions|
    initial_points : int, optional
        Number of initial random points to use to solve the concave problem.
        Default value is 1.
    max_iterations : int, optional
        Maximum number of iterations. Should be larger than initial_points.
        Default value is 30.
    solver : cvxpy.Solver, optional
        Solver used to solve the problem. Default solver is ECOS
    debug : bool, optional
        If true, prints the solver output.
    pi0 : np.ndarray, optional
        If a policy pi0 is provided, then we optimize over pi1
        the problem max_{pi1} V(pi1) - lambda I_F(pi0,pi1).
        In this case rho is set to 1 for simplicity.
    Returns
    -------
    I_L : float
        Inverse of the privacy level
    xi1, xi0 : np.ndarray
        Stationary distributions over states and actions achieving the best
        level of utility-privacy
    """

    # Sanity checks
    P0, P1 = sanity_check_probabilities(P0, P1)
    R0, R1 = sanity_check_rewards(R0, R1)
    initial_points = int(initial_points) if initial_points >= 1 else 1
    max_iterations = initial_points if initial_points > max_iterations else int(
        max_iterations)

    if rho < 0 or rho > 1:
        raise ValueError('Rho should be in [0,1]')

    if lmbd < 0:
        raise ValueError('Lambda should be non-negative')

    na = P0.shape[0]
    ns = P1.shape[1]

    if pi0 is not None:
        _xi0, _ = compute_stationary_distribution(P0, pi0)
        rho = 1

    best_res, best_xi1, best_xi0 = np.inf, None, None

    # Loop through initial points and return best result
    i = 0
    n = 0
    while i == 0 or (i < initial_points and n < max_iterations):
        n += 1

        # Construct the problem to find minimum privacy
        gamma = cp.Variable(1, nonneg=True)
        xi0 = cp.Variable((ns, na), nonneg=True) if pi0 is None else _xi0
        xi1 = cp.Variable((ns, na), nonneg=True)

        kl_div_stationary_dis = 0
        for s in range(ns):
            kl_div_stationary_dis += cp.kl_div(cp.sum(
                xi1[s, :]), cp.sum(xi0[s, :])) + cp.sum(xi1[s, :]) - cp.sum(
                    xi0[s, :])
        objective = gamma - lmbd * kl_div_stationary_dis

        # stationarity constraints
        stationarity_constraint0 = 0
        stationarity_constraint1 = 0
        for a in range(na):
            stationarity_constraint0 += xi0[:,
                                            a].T @ (P0[a, :, :] - np.eye(ns))
            stationarity_constraint1 += xi1[:,
                                            a].T @ (P1[a, :, :] - np.eye(ns))

        constraints = [stationarity_constraint1 == 0, cp.sum(xi1) == 1]

        if pi0 is None:
            constraints += [cp.sum(xi0) == 1, stationarity_constraint0 == 0]

        # Privacy-utility constraints
        privacy_utility_constraint = 0
        for s in range(ns):
            for y in range(ns):
                privacy_utility_constraint += lmbd * (
                    cp.kl_div(xi1[s, :] @ P1[:, s, y], xi0[s, :] @ P0[:, s, y])
                    + (xi1[s, :] @ P1[:, s, y]) - (xi0[s, :] @ P0[:, s, y]))
            for a in range(na):
                privacy_utility_constraint -= (
                    rho * xi1[s, a] * R1[s, a] +
                    (1 - rho) * xi0[s, a] * R0[s, a])

        constraints += [privacy_utility_constraint <= gamma]

        # Solve problem
        problem = cp.Problem(cp.Minimize(objective), constraints)
        if not dccp.is_dccp(problem):
            raise Exception('Problem is not Concave with convex constraints!')
        try:
            result = problem.solve(method='dccp',
                                   ccp_times=1,
                                   verbose=debug,
                                   solver=solver)
        except Exception as err:
            continue

        # Check if results are better than previous ones
        if result[0] is not None:
            i += 1
            if result[0] < best_res:
                best_res, best_xi1, best_xi0 = result[0], xi1.value, \
                    xi0.value if pi0 is None else xi0

    # Make sure to normalize the results
    best_xi0 += eps
    best_xi1 += eps
    best_xi0 /= np.sum(best_xi0) if not np.isclose(np.sum(best_xi0), 0) else 1.
    best_xi1 /= np.sum(best_xi1) if not np.isclose(np.sum(best_xi1), 0) else 1.
    return best_res, best_xi1, best_xi0
Пример #7
0
def limited_information_privacy_lb(P0: np.ndarray,
                                   P1: np.ndarray,
                                   initial_points: int = 1,
                                   max_iterations: int = 30,
                                   solver=cp.ECOS,
                                   debug=False):
    """ Computes the policies that achieves the best level of privacy in the
    limited information setting
    Parameters
    ----------
    P0, P1 : np.ndarray
        Numpy matrices containing the transition probabilities for models M0 and M1
        Each matrix should have dimensions |actions|x|states|x|states|
    initial_points : int, optional
        Number of initial random points to use to solve the concave problem.
        Default value is 1.
    max_iterations : int, optional
        Maximum number of iterations. Should be larger than initial_points.
        Default value is 30.
    solver : cvxpy.Solver, optional
        Solver used to solve the problem. Default solver is ECOS
    debug : bool, optional
        If true, prints the solver output.
    Returns
    -------
    I_L : float
        Inverse of the privacy level
    xi1, xi0 : np.ndarray
        Stationary distributions over states and actions achieving the best
        level of privacy
    """
    P0, P1 = sanity_check_probabilities(P0, P1)
    initial_points = int(initial_points) if initial_points >= 1 else 1
    max_iterations = initial_points if initial_points > max_iterations else int(
        max_iterations)

    na, ns = P0.shape[0], P0.shape[1]

    best_res, best_xi1, best_xi0 = np.inf, None, None
    # Compute KL divergences
    I = compute_KL_divergence_models(P0, P1)

    # Loop through initial points and return best result
    i = 0
    n = 0
    while i == 0 or (i < initial_points and n < max_iterations):
        n += 1
        gamma = cp.Variable(1)
        xi0 = cp.Variable((ns, na), nonneg=True)
        xi1 = cp.Variable((ns, na), nonneg=True)

        kl_div_statinary_dis = 0
        for s in range(ns):
            kl_div_statinary_dis += cp.entr(cp.sum(xi1[s, :]))

        # stationarity constraints
        stationarity_constraint = 0
        for a in range(na):
            stationarity_constraint += xi1[:, a].T @ (P1[a, :, :] - np.eye(ns))

        constraints = [stationarity_constraint == 0, cp.sum(xi1) == 1]

        # Privacy constraints
        privacy_constraint = 0
        for s in range(ns):
            constraints += [cp.sum(xi0[s, :]) == 1]
            for y in range(ns):
                privacy_constraint += cp.kl_div(
                    xi1[s, :] @ P1[:, s, y], xi0[s, :] @ P0[:, s, y]) + (
                        xi1[s, :] @ P1[:, s, y]) - (xi0[s, :] @ P0[:, s, y])

        constraints += [privacy_constraint <= gamma]
        objective = gamma + kl_div_statinary_dis

        # Solve problem
        problem = cp.Problem(cp.Minimize(objective), constraints)
        if not dccp.is_dccp(problem):
            raise Exception('Problem is not Concave with convex constraints!')
        try:
            result = problem.solve(method='dccp',
                                   ccp_times=1,
                                   verbose=debug,
                                   solver=solver)
        except Exception as err:
            continue

        # Check if results are better than previous ones
        if result[0] is not None:
            i += 1
            if result[0] < best_res:
                best_res, best_xi1, best_xi0 = result[0], xi1.value, xi0.value

    # Make sure to normalize the results
    best_xi0 += eps
    best_xi1 += eps
    best_xi0 /= np.sum(best_xi0) if not np.isclose(np.sum(best_xi0), 0) else 1.
    best_xi1 /= np.sum(best_xi1) if not np.isclose(np.sum(best_xi1), 0) else 1.
    return best_res, best_xi1, best_xi0