def test_kl_div(self): """Test a problem with kl_div. """ import numpy as np import cvxpy as cp kK=50 kSeed=10 prng=np.random.RandomState(kSeed) #Generate a random reference distribution npSPriors=prng.uniform(0.0,1.0,(kK,1)) npSPriors=npSPriors/sum(npSPriors) #Reference distribution p_refProb=cp.Parameter(kK,1,sign='positive') #Distribution to be estimated v_prob=cp.Variable(kK,1) objkl=0.0 for k in xrange(kK): objkl += cp.kl_div(v_prob[k,0],p_refProb[k,0]) constrs=[sum([v_prob[k,0] for k in xrange(kK)])==1] klprob=cp.Problem(cp.Minimize(objkl),constrs) p_refProb.value=npSPriors result = klprob.solve(solver=SCS, verbose=True) self.assertItemsAlmostEqual(v_prob.value, npSPriors)
def solver(r_b, eps = 0.1): pi_bn = cp.Variable(len(r_b)) uniform = np.ones_like(r_b)/len(r_b) constraints = [] constraints.append(pi_bn>=0) constraints.append(cp.sum(pi_bn) == 1) constraints.append(cp.kl_div(pi_bn, uniform)<=eps) obj = cp.Minimize([email protected](pi_bn)) prob = cp.Problem(obj, constraints) prob.solve(cp.ECOS) return pi_bn.value.tolist()
def test_mosek_exp(self): """ Formulate the following exponential cone problem with cvxpy min 3 * x[0] + 2 * x[1] + x[2] s.t. 0.1 <= x[0] + x[1] + x[2] <= 1 x >= 0 x[0] >= x[1] * exp(x[2] / x[1]) and solve with MOSEK and ECOS. Ensure that MOSEK and ECOS have the same primal and dual solutions. Note that the exponential cone constraint can be rewritten in terms of the relative entropy cone. The correspondence is as follows: x[0] >= x[1] * exp(x[2] / x[1]) iff x[1] * log(x[1] / x[0]) + x[2] <= 0. """ if cvx.MOSEK in cvx.installed_solvers(): import mosek if hasattr(mosek.conetype, 'pexp'): # Formulate and solve the problem with CVXPY x = cvx.Variable(shape=(3, 1)) constraints = [ sum(x) <= 1.0, sum(x) >= 0.1, x >= 0.01, cvx.kl_div(x[1], x[0]) + x[1] - x[0] + x[2] <= 0 ] obj = cvx.Minimize(3 * x[0] + 2 * x[1] + x[0]) prob = cvx.Problem(obj, constraints) prob.solve(solver=cvx.MOSEK) val_mosek = prob.value x_mosek = x.value.flatten().tolist() duals_mosek = [c.dual_value for c in constraints] prob.solve(solver=cvx.ECOS) val_ecos = prob.value x_ecos = x.value.flatten().tolist() duals_ecos = [c.dual_value for c in constraints] # verify results self.assertAlmostEqual(val_mosek, val_ecos) self.assertItemsAlmostEqual(x_mosek, x_ecos, places=4) self.assertEqual(len(duals_ecos), len(duals_mosek)) for i in range(len(duals_mosek)): if isinstance(duals_mosek[i], float): self.assertAlmostEqual(duals_mosek[i], duals_ecos[i], places=4) else: self.assertItemsAlmostEqual(duals_mosek[i].tolist(), duals_ecos[i].tolist(), places=4) else: pass
def kl_inverse(q, c): '''Compute kl inverse using Relative Entropy Programming''' p_bernoulli = cvx.Variable(2) q_bernoulli = np.array([q, 1 - q]) constraints = [ c >= cvx.sum(cvx.kl_div(q_bernoulli, p_bernoulli)), 0 <= p_bernoulli[0], p_bernoulli[0] <= 1, p_bernoulli[1] == 1.0 - p_bernoulli[0] ] prob = cvx.Problem(cvx.Maximize(p_bernoulli[0]), constraints) prob.solve(verbose=False, solver=cvx.ECOS) return p_bernoulli.value[0]
def lr_method(h): # parameters and equations o = 100 p = 3 u = 0.51 ki = 10**( -2) # increase the value of ki because the original value is too small B = 2 * 10**6 Vu = 1.1 N0 = 10**(-10) wi = np.array([1 if i % 2 == 1 else 1 for i in range(len(h)) ]) # default weights [1, 1.5, 1, 1.5, 1, 1.5, ...] # optimization variables tau_i = cvx.Variable(len(h)) fi = cvx.Variable(len(h)) ei = cvx.Variable(len(h)) # optimization objective and constraints result = cvx.sum(-cvx.multiply(wi,fi)*10**6+cvx.multiply(wi,(cvx.kl_div(tau_i,(cvx.multiply(ei,h)+tau_i*N0)/N0)\ +tau_i-(cvx.multiply(ei,h)+tau_i*N0)/N0))*B/Vu/np.log(2)) objective = cvx.Minimize(result) constraints = [ tau_i >= 0.0, cvx.sum(tau_i) <= 1.0, ei >= 0, fi >= 0, ei + cvx.multiply(ki, fi**3) <= u * p * h * (1 - cvx.sum(tau_i)) ] prob = cvx.Problem(objective, constraints) rewards = prob.solve(solver=cvx.MOSEK) # solve the problem by MOSEK local_rate = wi * (fi.value) * 10**6 offloading_rate = wi * B / Vu * tau_i.value * np.log2(1 + ei.value * h / (N0 * tau_i.value)) mode = [] for i in range(len(h)): if local_rate[i] < offloading_rate[i]: mode.append(1) else: mode.append(0) # compute the sum_rate with binary offloading E = u * p * h * (1 - np.sum(tau_i.value)) sum_rate = 0 for i in range(len(mode)): if mode[i] == 1: sum_rate += wi[i] * B / Vu * tau_i.value[i] * np.log2( 1 + E[i] * h[i] / (N0 * tau_i.value[i])) else: sum_rate += wi[i] * (E[i] / ki)**(1 / 3) * 10**6 return sum_rate, mode
def kl_inverse(q, c): p_bernoulli = cvx.Variable(2) q_bernoulli = np.array([q,1-q]) constraints = [c >= cvx.sum(cvx.kl_div(q_bernoulli,p_bernoulli)), 0 <= p_bernoulli[0], p_bernoulli[0] <= 1, p_bernoulli[1] == 1.0-p_bernoulli[0]] prob = cvx.Problem(cvx.Maximize(p_bernoulli[0]), constraints) # Solve problem opt = prob.solve(verbose=False, solver=solver=cvx.SCS) # solver=cvx.ECOS return p_bernoulli.value[0]
def optimal_power(n, a_val, b_val, P_tot: float = 1.0, W_tot: float = 1.0): ''' Boyd and Vandenberghe, Convex Optimization, exercise 4.62 page 210 Optimal power and bandwidth allocation in a Gaussian broadcast channel. We consider a communication system in which a central node transmits messages to n receivers. Each receiver channel is characterized by its (transmit) power level Pi ≥ 0 and its bandwidth Wi ≥ 0. The power and bandwidth of a receiver channel determine its bit rate Ri (the rate at which information can be sent) via Ri=αiWi log(1 + βiPi/Wi), where αi and βi are known positive constants. For Wi=0, we take Ri=0 (which is what you get if you take the limit as Wi → 0). The powers must satisfy a total power constraint, which has the form P1 + · · · + Pn = Ptot, where Ptot > 0 is a given total power available to allocate among the channels. Similarly, the bandwidths must satisfy W1 + · · · +Wn = Wtot, where Wtot > 0 is the (given) total available bandwidth. The optimization variables in this problem are the powers and bandwidths, i.e., P1, . . . , Pn, W1, . . . ,Wn. The objective is to maximize the total utility, sum(ui(Ri),i=1..n) where ui: R → R is the utility function associated with the ith receiver. ''' # Input parameters: alpha and beta are constants from R_i equation n = len(a_val) if n != len(b_val): print('alpha and beta vectors must have same length!') return 'failed', np.nan, np.nan, np.nan P = cvx.Variable(n) W = cvx.Variable(n) alpha = cvx.Parameter(n) beta = cvx.Parameter(n) alpha.value = np.array(a_val) beta.value = np.array(b_val) # This function will be used as the objective so must be DCP; i.e. element-wise multiplication must occur inside kl_div, not outside otherwise the solver does not know if it is DCP... R=cvx.kl_div(cvx.multiply(alpha, W), cvx.multiply(alpha, W + cvx.multiply(beta, P))) - \ cvx.multiply(alpha, cvx.multiply(beta, P)) objective = cvx.Minimize(cvx.sum(R)) constraints = [ P >= 0.0, W >= 0.0, cvx.sum(P) - P_tot == 0.0, cvx.sum(W) - W_tot == 0.0 ] prob = cvx.Problem(objective, constraints) prob.solve() return prob.status, -prob.value, P.value, W.value
def minimum_kl(p, Theta): n = len(p) q = cvxpy.Variable(shape=n) lp = cvxpy.Parameter(shape=n) pi = cvxpy.Parameter(shape=n) lp.value = np.log(p) pi.value = compute_beta(args.alpha, args.beta, Theta) constraints = [q >= 0.0, cvxpy.sum(q) == 1.0] R = cvxpy.kl_div(q, pi) - cvxpy.multiply(q, lp) objective = cvxpy.Minimize(cvxpy.sum(R)) prob = cvxpy.Problem(objective, constraints) try: prob.solve() except: prob.solve(solver="CVXOPT") return np.abs(q.value)
def optimal_power(n, a_val, b_val, P_tot=1.0, W_tot=1.0): ''' Boyd and Vandenberghe, Convex Optimization, exercise 4.62 page 210 Optimal power and bandwidth allocation in a Gaussian broadcast channel. We consider a communication system in which a central node transmits messages to n receivers. Each receiver channel is characterized by its (transmit) power level Pi ≥ 0 and its bandwidth Wi ≥ 0. The power and bandwidth of a receiver channel determine its bit rate Ri (the rate at which information can be sent) via Ri=αiWi log(1 + βiPi/Wi), where αi and βi are known positive constants. For Wi=0, we take Ri=0 (which is what you get if you take the limit as Wi → 0). The powers must satisfy a total power constraint, which has the form P1 + · · · + Pn = Ptot, where Ptot > 0 is a given total power available to allocate among the channels. Similarly, the bandwidths must satisfy W1 + · · · +Wn = Wtot, where Wtot > 0 is the (given) total available bandwidth. The optimization variables in this problem are the powers and bandwidths, i.e., P1, . . . , Pn, W1, . . . ,Wn. The objective is to maximize the total utility, sum(ui(Ri),i=1..n) where ui: R → R is the utility function associated with the ith receiver. ''' # Input parameters: alpha and beta are constants from R_i equation n=len(a_val) if n!=len(b_val): print('alpha and beta vectors must have same length!') return 'failed',np.nan,np.nan,np.nan P=cvx.Variable(n) W=cvx.Variable(n) alpha=cvx.Parameter(n) beta =cvx.Parameter(n) alpha.value=np.array(a_val) beta.value =np.array(b_val) # This function will be used as the objective so must be DCP; i.e. element-wise multiplication must occur inside kl_div, not outside otherwise the solver does not know if it is DCP... R=cvx.kl_div(cvx.mul_elemwise(alpha, W), cvx.mul_elemwise(alpha, W + cvx.mul_elemwise(beta, P))) - \ cvx.mul_elemwise(alpha, cvx.mul_elemwise(beta, P)) objective=cvx.Minimize(cvx.sum_entries(R)) constraints=[P>=0.0, W>=0.0, cvx.sum_entries(P)-P_tot==0.0, cvx.sum_entries(W)-W_tot==0.0] prob=cvx.Problem(objective, constraints) prob.solve() return prob.status,-prob.value,P.value,W.value
def minimum_kl(self, x): p = self.likelihood_estimator(x) n = len(p) q = cvxpy.Variable(shape=n) lp = cvxpy.Parameter(shape=n) pi = cvxpy.Parameter(shape=n) lp.value = np.log(p + 1e-8) pi.value = self.priors constraints = [q >= 0.0, cvxpy.sum(q) == 1.0] R = cvxpy.kl_div(q, pi) + q - pi - cvxpy.multiply(q, lp) objective = cvxpy.Minimize(cvxpy.sum(R)) prob = cvxpy.Problem(objective, constraints) try: prob.solve() except: prob.solve(solver="CVXOPT") return q.value
def test_expcone_2(self) -> None: x = cp.Variable(shape=(3,)) tempcons = [cp.sum(x) <= 1.0, cp.sum(x) >= 0.1, x >= 0.01, cp.kl_div(x[1], x[0]) + x[1] - x[0] + x[2] <= 0] sigma = cp.suppfunc(x, tempcons) y = cp.Variable(shape=(3,)) a = np.array([-3, -2, -1]) # this is negative of objective in mosek_conif.py example expr = -sigma(y) objective = cp.Maximize(expr) cons = [y == a] prob = cp.Problem(objective, cons) prob.solve(solver='ECOS') # Check for expected objective value epi_actual = prob.value direct_actual = expr.value expect = 0.235348211 self.assertLessEqual(abs(epi_actual - expect), 1e-6) self.assertLessEqual(abs(direct_actual - expect), 1e-6)
def relative_c_age(s, i): constraints = list() idx_set = np.arange(s.m) != i # variable definitions c_var = cvxpy.Variable(shape=(s.m, 1), name='c^{(' + str(i) + '})_' + str(s)) nu_var = cvxpy.Variable(shape=(s.m - 1, 1), name='nu^{(' + str(i) + '})_' + str(s), nonneg=True) # variable non-negativity constraints constraints.append(c_var[idx_set] >= 0) # main constraints constraints.append( (s.alpha[idx_set, :] - s.alpha[i, :]).T * nu_var == np.zeros(shape=(s.n, 1))) # convex cover constraint kl_expr1 = cvxpy.kl_div(nu_var, np.exp(1) * c_var[idx_set]) kl_expr2 = nu_var - np.exp(1) * c_var[idx_set] rel_ent = kl_expr1 + kl_expr2 constraints.append( cvxpy.sum(rel_ent) - c_var[i] <= 0) # relative entropy constraint return c_var, nu_var, constraints
def kl_inverse_r(c, q, params): #right kl inverse # KL(q||p) <= c # KLinv(c||p) = q # solve: sup q # s.t. KL(q||p) <= c p_bernoulli = cvx.Variable(2) q_bernoulli = np.array([q, 1 - q]) constraints = [ c >= cvx.sum(cvx.kl_div(p_bernoulli, q_bernoulli)), 0 <= p_bernoulli[0], p_bernoulli[0] <= 1, p_bernoulli[1] == 1.0 - p_bernoulli[0] ] prob = cvx.Problem(cvx.Maximize(p_bernoulli[0]), constraints) # Solve problem opt = prob.solve(verbose=params['verbose'], solver=params['solver']) return p_bernoulli.value[0]
def FTRL(T, MAB, eta=10, alg='exp_3'): K = len(MAB) S = np.zeros((K, )) losses = np.zeros((K, )) rewards = np.zeros((T, )) draws = 0 * rewards arms = np.linspace(0, K - 1, K, dtype='int') for t in trange(T): x = cp.Variable(K, pos=True) temp_1 = cp.Constant(value=np.ones((K, ))) temp_2 = cp.Constant(value=losses) constraints = [cp.sum(cp.multiply(temp_1, x)) == 1] if alg == 'log_barrier': obj = cp.Minimize( cp.sum(cp.multiply(temp_2, x)) - 1 / eta * cp.sum(cp.log(x))) elif alg == 'inf': obj = cp.Minimize( cp.sum(cp.multiply(temp_2, x)) - 2 / eta * cp.sum(cp.sqrt(x))) else: obj = cp.Minimize( cp.sum(cp.multiply(temp_2, x)) + 1 / eta * (cp.sum(cp.kl_div(x, temp_1)) - K)) pb = cp.Problem(obj, constraints) try: pb.solve() P = x.value except: P = np.ones((K, )) / K # print('Probability distribution:', P) if not np.sum(P) == 1: P = P / np.sum(P) action = np.random.choice(arms, p=P) X = 1 * MAB[action].sample().squeeze() S[action] = S[action] + X / P[action] losses[action] = losses[action] + (-X) / P[action] rewards[t] = X draws[t] = action return rewards, draws
def CVXOPT_programming(self, M, S, K, costs, C): failed = 0 if not all(i >= 0 for i in costs): return np.zeros(S), np.zeros(M), 0, 1 # Define QP parameters (directly) G1 = C / self.parameters['tau_inv'] h1 = costs G2 = -np.identity(M) h2 = np.zeros(M) G = np.concatenate((G1, G2), axis=0) h = np.concatenate((h1, h2), axis=None) R = cvx.Variable(shape=(M, 1)) K = K.reshape((M, 1)) * self.parameters['tau_inv'] h = h.reshape((M + S, 1)) # Construct the QP, invoke solver obj = cvx.Minimize(cvx.sum(cvx.kl_div(K + 1e-10, R + 1e-10))) constraints = [G * R <= h] prob = cvx.Problem(obj, constraints) prob.solver_stats try: prob.solve(solver=cvx.ECOS, abstol=1e-12, reltol=1e-12, warm_start=True, verbose=False, max_iters=300) except: N = np.zeros(S) R = np.zeros(M) return R, N, 0, 1 # Extract optimal value and solution N = (constraints[0].dual_value)[0:S] R = R.value / self.parameters['tau_inv'] R = R.reshape(M) N = N.reshape(S) return R, N, prob.value, failed
def test_difference_kl_div_rel_entr(self) -> None: """A test showing the difference between kl_div and rel_entr """ x = cvx.Variable() y = cvx.Variable() kl_div_prob = cvx.Problem(cvx.Minimize(cvx.kl_div(x, y)), constraints=[x + y <= 1]) kl_div_prob.solve(solver=cvx.ECOS) self.assertItemsAlmostEqual(x.value, y.value) self.assertItemsAlmostEqual(kl_div_prob.value, 0) rel_entr_prob = cvx.Problem(cvx.Minimize(cvx.rel_entr(x, y)), constraints=[x + y <= 1]) rel_entr_prob.solve(solver=cvx.ECOS) """ Reference solution computed by passing the following command to Wolfram Alpha: minimize x*log(x/y) subject to {x + y <= 1, 0 <= x, 0 <= y} """ self.assertItemsAlmostEqual(x.value, 0.2178117, places=4) self.assertItemsAlmostEqual(y.value, 0.7821882, places=4) self.assertItemsAlmostEqual(rel_entr_prob.value, -0.278464)
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
def get_dS(W, p): """ Rate of Shannon entropy change, for rate matrix W and distribution p """ """ We rewrite -sum_{i,j} p_i W_ji ln p_j as the "KL-like" expression 1/tau sum_{i,j} p_i T_ji ln (p_i T_ji/p_j T_ji) where tau = -min_i W_ii is the fastest time scale in R and T_ji = delta_{ji} + tau W_ji is a conditional probability distribuiton. This lets us indicate to cvxpy that -sum_{i,j} p_i W_ji ln p_j is convex in p. """ tau = -1/np.min(np.diag(W)) T = np.eye(n) + tau*W assert(np.all(T>=0)) dS = 0. for i in range(n): for j in range(n): if i == j: continue if np.isclose(T[i,j],0): continue dS += cp.kl_div( T[i,j] * p[j], T[i,j] * p[i]) + T[i,j] * p[j] - T[i,j] * p[i] return dS / tau
def test_kl_div(self) -> None: """Test a problem with kl_div. """ kK = 50 kSeed = 10 prng = np.random.RandomState(kSeed) # Generate a random reference distribution npSPriors = prng.uniform(0.0, 1.0, kK) npSPriors = npSPriors / sum(npSPriors) # Reference distribution p_refProb = cvx.Parameter(kK, nonneg=True) # Distribution to be estimated v_prob = cvx.Variable(kK) objkl = cvx.sum(cvx.kl_div(v_prob, p_refProb)) constrs = [cvx.sum(v_prob) == 1] klprob = cvx.Problem(cvx.Minimize(objkl), constrs) p_refProb.value = npSPriors klprob.solve(solver=cvx.SCS, verbose=True) self.assertItemsAlmostEqual(v_prob.value, npSPriors, places=3) klprob.solve(solver=cvx.ECOS, verbose=True) self.assertItemsAlmostEqual(v_prob.value, npSPriors)
def test_kl_div(self): """Test a problem with kl_div. """ kK = 50 kSeed = 10 prng = np.random.RandomState(kSeed) # Generate a random reference distribution npSPriors = prng.uniform(0.0, 1.0, (kK, 1)) npSPriors = npSPriors/sum(npSPriors) # Reference distribution p_refProb = cp.Parameter((kK, 1), nonneg=True) # Distribution to be estimated v_prob = cp.Variable((kK, 1)) objkl = 0.0 for k in range(kK): objkl += cp.kl_div(v_prob[k, 0], p_refProb[k, 0]) constrs = [sum(v_prob[k, 0] for k in range(kK)) == 1] klprob = cp.Problem(cp.Minimize(objkl), constrs) p_refProb.value = npSPriors klprob.solve(solver=cp.SCS) self.assertItemsAlmostEqual(v_prob.value, npSPriors)
def relative_c_sage_star(s, v): """ Given the Signomial s and a CVXPY variable "v", return a list of CVXPY Constraint objects such that v is a conic dual variable to the constraint "s.c \in C_{SAGE}(s.alpha)". :param s: a Signomial object :param v: a CVXPY Variable with v.size == s.m. :return a list of CVXPY Constraint objects. Remark 1: The CVXPY function kl_div operates in a way that differs from the relative entropy function as described in the literature on SAGE relaxations. Refer to the CVXPY documentation if our usage seems odd. Remark 2: This implementation is vectorized to minimize the length of the list "constraints". By doing this we significantly speed up the process of CVXPY converting our problem to its internal standard form. """ alpha, c = s.alpha_c_arrays() if s.m <= 2: return [v >= 0] non_constants = [ i for i, c_i in enumerate(c) if not isinstance(c_i, __NUMERIC_TYPES__) ] N_I = [i for i, c_i in enumerate(c) if (i in non_constants) or c_i < 0] Nc_I = [i for i, c_i in enumerate(c) if (i in non_constants) or c_i > 0] # variable definitions mu = cvxpy.Variable(shape=(len(N_I), s.n), name=('mu_' + str(v.id))) # constraints constraints = [] for i, ii in enumerate(N_I): # i = the index used for "mu", ii = index used for alpha and v j_neq_ii = [j for j in Nc_I if j != ii] expr1 = v[ii] * np.ones((len(j_neq_ii), 1)) expr2 = cvxpy.kl_div(expr1, v[j_neq_ii]) + expr1 - v[j_neq_ii] expr3 = (alpha[ii, :] - alpha[j_neq_ii, :]) * mu[i, :].T constraints.append(expr2 <= cvxpy.reshape(expr3, (len(j_neq_ii), 1))) constraints.append(v[list(set(N_I + Nc_I))] >= 0) return constraints
def OptimizeWell(well_info, supply='external', tol=1e-7, shift_size=1, eps=1e-20, alpha=0.5, R0t_0=10, verbose=False, max_iters=1000): """ Uses convex optimization to find the steady state of the ecological dynamics. """ #UNPACK INPUT y0 = well_info['y0'].copy() params_comp = well_info['params'].copy() N = y0[:params_comp['S']] R = y0[params_comp['S']:] #COMPRESS PARAMETERS TO GET RID OF EXTINCT SPECIES not_extinct_consumers = N > 0 if supply == 'external': not_extinct_resources = np.ones(len(R), dtype=bool) else: not_extinct_resources = R > 0 params_comp['c'] = params_comp['c'][not_extinct_consumers, :] params_comp['c'] = params_comp['c'][:, not_extinct_resources] params_comp['D'] = params_comp['D'][not_extinct_resources, :] params_comp['D'] = params_comp['D'][:, not_extinct_resources] for name in ['m', 'g', 'K']: if name in params_comp.keys(): if type(params_comp[name]) == np.ndarray: assert len( params_comp[name]) == len(N), 'Invalid length for ' + name params_comp[name] = params_comp[name][not_extinct_consumers] for name in ['l', 'w', 'r', 'tau', 'R0']: if name in params_comp.keys(): if type(params_comp[name]) == np.ndarray: assert len( params_comp[name]) == len(R), 'Invalid length for ' + name params_comp[name] = params_comp[name][not_extinct_resources] S = len(params_comp['c']) M = len(params_comp['c'].T) failed = 0 if params_comp['l'] != 0: assert supply == 'external', 'Replenishment must be external for crossfeeding dynamics.' #Make Q matrix and effective weight vector w_mat = np.kron(np.ones((M, 1)), np.ones((1, M)) * params_comp['w']) Q = np.eye(M) - params_comp['l'] * params_comp['D'] * w_mat / (w_mat.T) Qinv = np.linalg.inv(Q) Qinv_aa = np.diag(Qinv) w = Qinv_aa * ( 1 - params_comp['l']) * params_comp['w'] / params_comp['tau'] Qinv = Qinv - np.diag(Qinv_aa) #Construct variables for optimizer G = params_comp['c'] * params_comp['w'] * ( 1 - params_comp['l']) / w #Divide by w, because we will multiply by wR if isinstance(params_comp['m'], np.ndarray): h = params_comp['m'].reshape((S, 1)) else: h = np.ones((S, 1)) * params_comp['m'] #Initialize effective resource concentrations R0t = R0t_0 * np.ones(M) #Set up the loop Rf = np.inf Rf_old = 0 k = 0 ncyc = 0 Delta = 1 Delta_old = 1 while np.linalg.norm(Rf_old - Rf) > tol and k < max_iters: try: start_time = time.time() wR = cvx.Variable(shape=(M, 1)) #weighted resources #Need to multiply by w to get properly weighted KL divergence R0t = np.sqrt(R0t**2 + eps) wR0 = (R0t * w).reshape((M, 1)) #Solve obj = cvx.Minimize(cvx.sum(cvx.kl_div(wR0, wR))) constraints = [G * wR <= h, wR >= 0] prob = cvx.Problem(obj, constraints) prob.solver_stats prob.solve(solver=cvx.ECOS, abstol=1e-8, reltol=1e-8, warm_start=True, verbose=False, max_iters=5000) #Record the results Rf_old = Rf Nf = constraints[0].dual_value[0:S].reshape(S) Rf = wR.value.reshape(M) / w #Update the effective resource concentrations R0t_new = params_comp['R0'] + Qinv.dot( (params_comp['R0'] - Rf) / params_comp['tau']) * (params_comp['tau'] / Qinv_aa) Delta_R0t = R0t_new - R0t R0t = R0t + alpha * Delta_R0t Delta_old = Delta Delta = np.linalg.norm(Rf_old - Rf) if verbose: print('Iteration: ' + str(k)) print('Delta: ' + str(Delta)) print('---------------- ' + str(time.time() - start_time)[:4] + ' s ----------------') except: #If optimization fails, try new R0t shift = shift_size * np.random.randn(M) R0t = np.abs(R0t + shift) if verbose: print('Added ' + str(shift_size) + ' times random numbers') k += 1 #Check for limit cycle if np.isfinite(Delta) and Delta > tol and np.abs( Delta - Delta_old) < 0.1 * tol: ncyc += 1 if ncyc > 10: print('Limit cycle detected') k = max_iters if k == max_iters: failed = 1 else: if verbose: print('success') elif params_comp['l'] == 0: if supply == 'external': G = params_comp['c'] * params_comp[ 'tau'] #Multiply by tau, because wR has tau in the denominator if isinstance(params_comp['m'], np.ndarray): h = params_comp['m'].reshape((S, 1)) else: h = np.ones((S, 1)) * params_comp['m'] wR = cvx.Variable(shape=(M, 1)) #weighted resources #Need to multiply by w to get properly weighted KL divergence wR0 = (params_comp['R0'] * params_comp['w'] * np.ones(M) / params_comp['tau']).reshape((M, 1)) #Solve obj = cvx.Minimize(cvx.sum(cvx.kl_div(wR0, wR))) constraints = [G * wR <= h] prob = cvx.Problem(obj, constraints) prob.solver_stats prob.solve(solver=cvx.ECOS, abstol=1e-8, reltol=1e-8, warm_start=True, verbose=False, max_iters=5000) #Record the results Nf = constraints[0].dual_value[0:S].reshape(S) Rf = wR.value.reshape(M) * params_comp['tau'] / params_comp['w'] elif supply == 'self-renewing': #Format constants and variables if isinstance(params_comp['m'], np.ndarray): h = params_comp['m'] else: h = np.ones(S) * params_comp['m'] if isinstance(params_comp['w'], np.ndarray): w = params_comp['w'] else: w = np.ones(M) * params_comp['w'] if isinstance(params_comp['r'], np.ndarray): r = params_comp['r'] else: r = np.ones(M) * params_comp['r'] R0 = params_comp['R0'] R_opt = cvx.Variable(M) #Make constraints G = params_comp['c'] * params_comp['w'] G = np.vstack((G, -np.eye(M))) h = np.hstack((h, np.zeros(M))) #Solve obj = cvx.Minimize( (1 / 2) * cvx.quad_form(R0 - R_opt, np.diag(w * r))) constraints = [G @ R_opt <= h] prob = cvx.Problem(obj, constraints) prob.solve(solver=cvx.ECOS, abstol=1e-9, feastol=1e-7, abstol_inacc=1e-5, feastol_inacc=1e-5, max_iters=5000) #Record the results Nf = constraints[0].dual_value[:S] Rf = R_opt.value elif supply == 'predator': #Format constants and variables if isinstance(params_comp['m'], np.ndarray): h = params_comp['m'] else: h = np.ones(S) * params_comp['m'] if isinstance(params_comp['w'], np.ndarray): w = params_comp['w'] else: w = np.ones(M) * params_comp['w'] r = params_comp['r'] u = params_comp['u'] R0 = params_comp['R0'] R_opt = cvx.Variable(M) #Make constraints G = params_comp['c'] * params_comp['w'] G = np.vstack((G, -np.eye(M))) h = np.hstack((h, np.zeros(M))) #Solve obj = cvx.Minimize((1 / 2) * cvx.quad_form(R0 - R_opt, np.diag(w * r)) + u.T @ R_opt) constraints = [G @ R_opt <= h] prob = cvx.Problem(obj, constraints) prob.solve(solver=cvx.ECOS, abstol=1e-9, feastol=1e-7, abstol_inacc=1e-5, feastol_inacc=1e-5, max_iters=5000) #Record the results Nf = constraints[0].dual_value[:S] Rf = R_opt.value else: print('supply must be external or self-renewing') failed = True if not failed: N_new = np.zeros(len(N)) R_new = np.zeros(len(R)) N_new[np.where(not_extinct_consumers)[0]] = Nf R_new[np.where(not_extinct_resources)[0]] = Rf else: N_new = np.nan * N R_new = np.nan * R if verbose: print('Optimization Failed.') return np.hstack((N_new, R_new))
prox("NORM_2", lambda: cp.norm(X, "fro")), prox("NORM_2", lambda: cp.norm2(x)), prox("NORM_NUCLEAR", lambda: cp.norm(X, "nuc")), prox("SECOND_ORDER_CONE", None, C_soc_scaled), prox("SECOND_ORDER_CONE", None, C_soc_scaled_translated), prox("SECOND_ORDER_CONE", None, C_soc_translated), prox("SECOND_ORDER_CONE", None, lambda: [cp.norm(X, "fro") <= t]), prox("SECOND_ORDER_CONE", None, lambda: [cp.norm2(x) <= t]), prox("SEMIDEFINITE", None, lambda: [X >> 0]), prox("SUM_DEADZONE", f_dead_zone), prox("SUM_EXP", lambda: cp.sum_entries(cp.exp(x))), prox("SUM_HINGE", f_hinge), prox("SUM_HINGE", lambda: cp.sum_entries(cp.max_elemwise(1-x, 0))), prox("SUM_HINGE", lambda: cp.sum_entries(cp.max_elemwise(1-x, 0))), prox("SUM_INV_POS", lambda: cp.sum_entries(cp.inv_pos(x))), prox("SUM_KL_DIV", lambda: cp.sum_entries(cp.kl_div(p1,q1))), prox("SUM_LARGEST", lambda: cp.sum_largest(x, 4)), prox("SUM_LOGISTIC", lambda: cp.sum_entries(cp.logistic(x))), prox("SUM_NEG_ENTR", lambda: cp.sum_entries(-cp.entr(x))), prox("SUM_NEG_LOG", lambda: cp.sum_entries(-cp.log(x))), prox("SUM_QUANTILE", f_quantile), prox("SUM_QUANTILE", f_quantile_elemwise), prox("SUM_SQUARE", f_least_squares_matrix), prox("SUM_SQUARE", lambda: f_least_squares(20)), prox("SUM_SQUARE", lambda: f_least_squares(5)), prox("SUM_SQUARE", f_quad_form), prox("TOTAL_VARIATION_1D", lambda: cp.tv(x)), prox("ZERO", None, C_linear_equality), prox("ZERO", None, C_linear_equality_matrix_lhs), prox("ZERO", None, C_linear_equality_matrix_rhs), prox("ZERO", None, C_linear_equality_multivariate),
def CVXOPT_crossfeeding(self, S, M, K, C, D, e, costs,tol=1e-7,shift_size=1,eps=1e-20, alpha=0.5,R0t_0=10,verbose=False,max_iters=1000): Q = np.eye(M) - (1-e)*D Qinv = np.linalg.inv(Q) Qinv_aa = np.diag(Qinv) w = Qinv_aa*e Qinv = Qinv - np.diag(Qinv_aa) #Construct variables for optimizer G = C*e/w h = costs.reshape((S,1)) #Set up the loop Rf = np.inf Rf_old = 0 k=0 ncyc=0 Delta = 1 Delta_old=1 failed = 0 R0t=K while np.linalg.norm(Rf_old - Rf) > tol and k < max_iters: try: start_time = time.time() wR = cvx.Variable(shape=(M,1)) #weighted resources #Need to multiply by w to get properly weighted KL divergence R0t = np.sqrt(R0t**2+eps) wR0 = (R0t*w).reshape((M,1)) #Solve obj = cvx.Minimize(cvx.sum(cvx.kl_div(wR0, wR))) constraints = [G*wR <= h, wR >= 0] prob = cvx.Problem(obj, constraints) prob.solver_stats prob.solve(solver=cvx.ECOS,abstol=0.1*tol,reltol=0.1*tol,warm_start=True,verbose=False,max_iters=200) #Record the results Rf_old = Rf Nf=constraints[0].dual_value[0:S].reshape(S) Rf=wR.value.reshape(M)/w #Update the effective resource concentrations R0t_new = K+ Qinv.dot(K-Rf)/Qinv_aa Delta_R0t = R0t_new-R0t R0t = R0t + alpha*Delta_R0t Delta_old = Delta Delta = np.linalg.norm(Rf_old - Rf) if verbose: print('Iteration: '+str(k)) print('Delta: '+str(Delta)) print('---------------- '+str(time.time()-start_time)[:4]+' s ----------------') except: #If optimization fails, try new R0t shift = shift_size*np.random.randn(M) if np.min(R0t + shift) < 0: #Prevent any values from becoming negative R0t = R0t_0*np.ones(M) Rf = np.inf Rf_old = 0 else: R0t = R0t + shift if verbose: print('Added '+str(eps)+' times random numbers') k+=1 #Check for limit cycle if np.isfinite(Delta) and Delta > tol and np.abs(Delta-Delta_old) < 0.1*tol: ncyc+=1 if ncyc > 10: print('Limit cycle detected') k = max_iters if k == max_iters: failed = 1 return Rf, Nf, failed
def attacked_FTRL(T, MAB, target_arm, eta=10, alg='exp_3', delta=0.99, constant_attack=False): K = len(MAB) true_S = np.zeros((K, )) true_losses = np.zeros((K, )) N = np.zeros((K, )) estimated_losses = np.zeros((K, )) rewards = np.zeros((T, )) draws = 0 * rewards arms = np.linspace(0, K - 1, K, dtype='int') attacks = np.zeros((T, )) time_of_attacks = np.zeros((T, )) for t in trange(T): x = cp.Variable(K, pos=True) temp_1 = cp.Constant(value=np.ones((K, ))) temp_2 = cp.Constant(value=estimated_losses) constraints = [cp.sum(cp.multiply(temp_1, x)) == 1] if alg == 'log_barrier': obj = cp.Minimize( cp.sum(cp.multiply(temp_2, x)) - 1 / eta * cp.sum(cp.log(x))) elif alg == 'inf': obj = cp.Minimize( cp.sum(cp.multiply(temp_2, x)) - 2 / eta * cp.sum(cp.sqrt(x))) else: obj = cp.Minimize( cp.sum(cp.multiply(temp_2, x)) + 1 / eta * (cp.sum(cp.kl_div(x, temp_1)) - K)) pb = cp.Problem(obj, constraints) try: pb.solve() P = x.value except: P = np.ones((K, )) / K # print("\nThe optimal value is", pb.value) # print("A solution x is") # print(x.value) # print("A dual solution corresponding to the inequality constraints is") # print(pb.constraints[0].dual_value) # print('Probability distribution:', P) if not np.sum(P) == 1: P = P / np.sum(P) if t < K: action = t attack_t = 0 else: action = np.random.choice(arms, p=P) if action != target_arm: time_of_attacks[t] = 1 beta = np.sqrt( np.log(np.pi**2 * K * N**2 / (3 * delta)) / (2 * N)) if constant_attack: attack_t = -2 * np.maximum( 0, MAB[action].mean - MAB[target_arm].mean) else: attack_t = -np.maximum( (true_S / N)[action] - (true_S / N)[target_arm] + beta[action] + beta[target_arm], 0) else: attack_t = 0 attacks[t] = attack_t true_X = 1 * MAB[action].sample().squeeze() X = true_X + attack_t true_S[action] = true_S[action] + true_X true_losses[action] = true_losses[action] + (1 - true_X) / P[action] estimated_losses[action] = estimated_losses[action] + (1 - X) / P[action] N[action] = N[action] + 1 rewards[t] = true_X draws[t] = action return rewards, draws, attacks, time_of_attacks
def kl_ucb_zpd(progression_graph, trace_problems_dict, kl_ucb_lower_threshold, kl_ucb_n0, kl_ucb_n1, timeout, p_threshold, student): def is_correct(problem, answer): return answer == problem[2] traces = set(progression_graph.keys()) traces.remove("") # Create nodes to store algorithm-specific trace details. trace_node_dict = { trace: KL_UCB_node(kl_ucb_n0, kl_ucb_n1) for trace in traces } trace_node_dict[""] = KL_UCB_node(0, 1) # Jon Snow knows nothing. log = f'Trace -> KL-UCB Node dictionary created.' logger.info(log) # Create a dependency graph of traces. (Reverse of the progression graph.) dependency_graph = {} for trace in progression_graph.keys(): dependency_graph[trace] = [] for trace, more_complex_traces in progression_graph.items(): for i in more_complex_traces: dependency_graph[i].append(trace) log = f"Generated dependency graph: " for trace, less_complex_trace in dependency_graph.items(): log += f"\n{trace} -> {less_complex_trace}" logger.info(log) # Create two maps One from trace to traces less-complex than it. Another # from trace to traces more-complex than it. trace_to_less_complex_traces_dict = {} trace_to_more_complex_traces_dict = {} for trace in traces: trace_to_less_complex_traces_dict[trace] = set() trace_to_more_complex_traces_dict[trace] = set() for trace in traces: unadded_set = set(progression_graph[trace]) more_complex_traces_set = set() while len(unadded_set) > 0: elem = unadded_set.pop() unadded_set |= set(progression_graph[elem]) more_complex_traces_set.add(elem) trace_to_less_complex_traces_dict[elem].add(trace) trace_to_more_complex_traces_dict[trace] = more_complex_traces_set log = f"Generated map from trace to less complex traces: " for t, lct in trace_to_less_complex_traces_dict.items(): log += f"\n{t} -> {lct}" logger.info(log) log = f"Generated map from trace to more complex traces: " for t, mct in trace_to_more_complex_traces_dict.items(): log += f"\n{t} -> {mct}" logger.info(log) # Do topological ordering of the progression graph. sorted_traces = sorted(traces, key=cmp_to_key(cmp_trace)) log = f'Generalised topological sort of the traces.\n' for trace in sorted_traces: log += f'\nTrace - {trace}' logger.info(log) # Run the kl-ucb bandit algorithm. # t = 1 is over when initialising the nodes. t = 2 while t < timeout: # Create a dict that maps each max entropy value to the corresponding # traces. fp_traces_dict = defaultdict(set) for trace in traces: node = trace_node_dict[trace] kl_limit = (math.log(1 + t * math.log(math.log(t, 2), 2), 2) / (node.n0_t0 + node.n1_t0)) # print(kl_limit) u = cp.Variable() kl_d = ( (cp.kl_div(node.p_t0, u) + cp.kl_div(1 - node.p_t0, 1 - u)) / math.log(2)) constraints = [kl_d <= kl_limit, u <= 1, u >= 0] # obj = cp.Maximize((cp.entr(u) + cp.entr(1 - u)) / math.log(2)) obj = cp.Minimize(u) # print(f"obj is dcp {obj.is_dcp()}") prob = cp.Problem(obj, constraints) prob.solve() # print(f"Trace {trace} and p:{u.value}") if prob.value < kl_ucb_lower_threshold: fp_traces_dict[prob.value].add(trace) if len(fp_traces_dict) == 0: logger.info(f"No traces above kl_ucb_lower_threshold") continue log = (f'f(p) for each trace calculated for round {t}.') for fp, ts in fp_traces_dict.items(): log += f'\nfp: {fp} -> Traces: {ts}' logger.info(log) # Select the trace with the maximum entropy among all traces, i.e. the # arm, in topological order. max_fp, max_fp_traces_set = itemgetter(0)(sorted( fp_traces_dict.items(), key=itemgetter(0), reverse=True)) for trace in sorted_traces: if trace in max_fp_traces_set: trace_arm = trace break # Ask the student a problem corresponding to the chosen trace and record # the answer. chosen_problem = trace_problems_dict[trace_arm][0] answer = student.solve(chosen_problem) node = trace_node_dict[trace_arm] log = (f'\nMax fp: {max_fp}\nMax traces: {max_fp_traces_set}' f'\nTrace-arm: {trace_arm}\nProblem: {chosen_problem}' f'\nAnswer: {answer}') logger.info(log) # Update the nodes of the algorithm according to the correctness of the # answer. if is_correct(chosen_problem, answer): node = trace_node_dict[trace_arm] node.n1_t1 = node.n1_t0 + 1 node.n0_t1 = node.n0_t0 node.p_t1 = node.n1_t1 / (node.n0_t1 + node.n1_t1) for trace in trace_to_less_complex_traces_dict[trace_arm]: node = trace_node_dict[trace] node.n1_t1 = node.n1_t0 + 1 node.n0_t1 = node.n0_t0 node.p_t1 = node.n1_t1 / (node.n0_t1 + node.n1_t1) else: # Calculate probability of answering by taking product of the # probabilities of the dependants of chosen trace. node = trace_node_dict[trace_arm] node.n1_t1 = node.n1_t0 node.n0_t1 = node.n0_t0 + 1 node.p_t1 = node.n1_t1 / (node.n0_t1 + node.n1_t1) for trace in trace_to_more_complex_traces_dict[trace_arm]: node = trace_node_dict[trace] node.n1_t1 = node.n1_t0 node.n0_t1 = node.n0_t0 + 1 node.p_t1 = node.n1_t1 / (node.n0_t1 + node.n1_t1) for trace, node in trace_node_dict.items(): node.update() log = f'Updation of nodes completed.' for trace, node in trace_node_dict.items(): log += f'\nTrace: {trace} -> Node: {node}' logger.info(log) if all([s == KC_STATE.LEARNED for s in student.kc_states.values()]): print("Yipe") break t += 1 init_zpd = set() for trace, dependencies in dependency_graph.items(): if trace_node_dict[trace].p_t0 <= p_threshold: dependency_threshold = [ trace_node_dict[t].p_t0 > p_threshold for t in dependencies ] for i, threshold in enumerate(dependency_threshold): if threshold: init_zpd.add(dependencies[i]) if any(dependency_threshold): init_zpd.add(trace) return init_zpd
def optimize_quad_PAC_bound_bisection(costs_precomputed, p0, delta): '''Performs REP on the quadratic PAC-Bayes bound by sweeping on L_hat, bisectional search on lambda''' # Number of actions L = len(p0) # Number of environments m = np.shape(costs_precomputed)[0] C_bar = (1 / m) * (np.ones((1, m)) @ costs_precomputed) min_cost = np.min(C_bar) max_cost = np.max(C_bar) L_hats = np.linspace(min_cost, max_cost, np.ceil((max_cost - min_cost) / 0.001)) R_p0 = np.log(2 * np.sqrt(m) / delta) / (2 * m) # Initialize vectors for storing optimal solutions tau_opt = ((C_bar @ p0 + R_p0)**0.5 + R_p0**0.5)**2 p_opt = p0 for j in range(len(L_hats)): terminate = False L_hat = L_hats[j] min_lambda0 = (L_hat * R_p0 + R_p0**2)**0.5 max_lambda0 = (tau_opt - L_hat) / 2 - R_p0 if min_lambda0 > max_lambda0: # If this happens then the prior gives a lower tau than any valid # lambda choice terminate = True while not terminate: lambda0 = (min_lambda0 + max_lambda0) / 2 # Create cost function variable tau = cvx.Variable() # Create variable for probability vector p = cvx.Variable(L) cost_empirical = (1 / m) * cvx.sum(costs_precomputed * p) R = (cvx.sum(cvx.kl_div(p, p0)) + np.log(2 * np.sqrt(m) / delta)) / (2 * m) # R = 0.0 # Constraints constraints = [ tau >= L_hat + 2 * R + 2 * lambda0, lambda0**2 >= L_hat * R + R**2, L_hat == cost_empirical, p >= 0, cvx.sum(p) == 1 ] prob = cvx.Problem(cvx.Minimize(tau), constraints) # Solve problem opt = prob.solve(verbose=False, solver=cvx.MOSEK) # , max_iters=3000) if np.isinf(opt) or (opt is None): min_lambda0 = lambda0 else: max_lambda0 = lambda0 if np.abs(lambda0 - (min_lambda0 + max_lambda0) / 2) < 0.001: terminate = True # Store optimal value and optimizer if (opt < tau_opt): tau_opt = opt p_opt = p.value new_emp_cost = (costs_precomputed @ p_opt).mean() return tau_opt, p_opt, new_emp_cost
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
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
prox("NORM_2", lambda: cp.norm2(x)), prox("NORM_NUCLEAR", lambda: cp.norm(X, "nuc")), #prox("QUAD_OVER_LIN", lambda: cp.quad_over_lin(p, q1)), prox("SECOND_ORDER_CONE", None, C_soc_scaled), prox("SECOND_ORDER_CONE", None, C_soc_scaled_translated), prox("SECOND_ORDER_CONE", None, C_soc_translated), prox("SECOND_ORDER_CONE", None, lambda: [cp.norm(X, "fro") <= t]), prox("SECOND_ORDER_CONE", None, lambda: [cp.norm2(x) <= t]), prox("SEMIDEFINITE", None, lambda: [X >> 0]), prox("SUM_DEADZONE", f_dead_zone), prox("SUM_EXP", lambda: cp.sum_entries(cp.exp(x))), prox("SUM_HINGE", f_hinge), prox("SUM_HINGE", lambda: cp.sum_entries(cp.max_elemwise(1-x, 0))), prox("SUM_HINGE", lambda: cp.sum_entries(cp.max_elemwise(1-x, 0))), prox("SUM_INV_POS", lambda: cp.sum_entries(cp.inv_pos(x))), prox("SUM_KL_DIV", lambda: cp.sum_entries(cp.kl_div(p1,q1))), prox("SUM_LARGEST", lambda: cp.sum_largest(x, 4)), prox("SUM_LOGISTIC", lambda: cp.sum_entries(cp.logistic(x))), prox("SUM_NEG_ENTR", lambda: cp.sum_entries(-cp.entr(x))), prox("SUM_NEG_LOG", lambda: cp.sum_entries(-cp.log(x))), prox("SUM_QUANTILE", f_quantile), prox("SUM_QUANTILE", f_quantile_elemwise), prox("SUM_SQUARE", f_least_squares_matrix), prox("SUM_SQUARE", lambda: f_least_squares(20)), prox("SUM_SQUARE", lambda: f_least_squares(5)), prox("SUM_SQUARE", f_quad_form), prox("TOTAL_VARIATION_1D", lambda: cp.tv(x)), prox("ZERO", None, C_linear_equality), prox("ZERO", None, C_linear_equality_matrix_lhs), prox("ZERO", None, C_linear_equality_matrix_rhs), prox("ZERO", None, C_linear_equality_multivariate),
def rel_H(x, y): # relative entropy in Boyd return cvx.sum(cvx.kl_div(x, y) + x - y)
u=0.51 ki=10**(-2) # increase the value of ki because the original value is too small B=2*10**6 Vu=1.1 N0=10**(-10) h=np.array([2.52690569892026e-06,1.44441867078275e-06,9.33368801933646e-07,2.23291617751371e-06,9.21244504161081e-07]) # optimization variables tau_i = cvx.Variable(len(h)) fi = cvx.Variable(len(h)) ei = cvx.Variable(len(h)) a = 1 - cvx.sum(tau_i) # optimization objective and constraints result = cvx.sum(-fi*10**6+(cvx.kl_div(tau_i,(cvx.multiply(ei,h)+tau_i*N0)/N0)\ +tau_i-(cvx.multiply(ei,h)+tau_i*N0)/N0)*B/Vu/np.log(2)) objective = cvx.Minimize(result) constraints = [tau_i >= 0,a >= 0,ei >= 0,fi >= 0,ei + cvx.multiply(ki,fi**3) <= u*p*h*a] prob = cvx.Problem(objective, constraints) rewards = prob.solve(solver = cvx.MOSEK) # solve the problem by MOSEK local_rate = (fi.value)*10**6 offloading_rate = B/Vu*tau_i.value*np.log2(1+ei.value*h/(N0*tau_i.value)) # mode = [] # for i in range(len(h)): # if local_rate[i] < offloading_rate[i]: # mode.append(1) # else: # mode.append(0)
prox("NORM_2", lambda: cp.norm2(x)), prox("NORM_NUCLEAR", lambda: cp.norm(X, "nuc")), #prox("QUAD_OVER_LIN", lambda: cp.quad_over_lin(p, q1)), prox("SECOND_ORDER_CONE", None, C_soc_scaled), prox("SECOND_ORDER_CONE", None, C_soc_scaled_translated), prox("SECOND_ORDER_CONE", None, C_soc_translated), prox("SECOND_ORDER_CONE", None, lambda: [cp.norm(X, "fro") <= t]), prox("SECOND_ORDER_CONE", None, lambda: [cp.norm2(x) <= t]), prox("SEMIDEFINITE", None, lambda: [X >> 0]), prox("SUM_DEADZONE", f_dead_zone), prox("SUM_EXP", lambda: cp.sum_entries(cp.exp(x))), prox("SUM_HINGE", f_hinge), prox("SUM_HINGE", lambda: cp.sum_entries(cp.max_elemwise(1 - x, 0))), prox("SUM_HINGE", lambda: cp.sum_entries(cp.max_elemwise(1 - x, 0))), prox("SUM_INV_POS", lambda: cp.sum_entries(cp.inv_pos(x))), prox("SUM_KL_DIV", lambda: cp.sum_entries(cp.kl_div(p1, q1))), prox("SUM_LARGEST", lambda: cp.sum_largest(x, 4)), prox("SUM_LOGISTIC", lambda: cp.sum_entries(cp.logistic(x))), prox("SUM_NEG_ENTR", lambda: cp.sum_entries(-cp.entr(x))), prox("SUM_NEG_LOG", lambda: cp.sum_entries(-cp.log(x))), prox("SUM_QUANTILE", f_quantile), prox("SUM_QUANTILE", f_quantile_elemwise), prox("SUM_SQUARE", f_least_squares_matrix), prox("SUM_SQUARE", lambda: f_least_squares(20)), prox("SUM_SQUARE", lambda: f_least_squares(5)), prox("SUM_SQUARE", f_quad_form), prox("TOTAL_VARIATION_1D", lambda: cp.tv(x)), prox("ZERO", None, C_linear_equality), prox("ZERO", None, C_linear_equality_matrix_lhs), prox("ZERO", None, C_linear_equality_matrix_rhs), prox("ZERO", None, C_linear_equality_multivariate),
def test_kl_div(self): """Test a problem with kl_div. """ import numpy as np import cvxpy as cp kK=10 kSeed=10 prng=np.random.RandomState(kSeed) #Generate a random reference distribution npSPriors=prng.uniform(0.0,1.0,kK) npSPriors=npSPriors/np.sum(npSPriors) #Reference distribution p_refProb=cp.Parameter(kK,1,sign='positive') #Distribution to be estimated v_prob=cp.Variable(kK,1) objkl=0.0 for k in xrange(kK): objkl += cp.kl_div(v_prob[k,0],p_refProb[k,0]) constrs=[sum([v_prob[k,0] for k in xrange(kK)])==1] klprob=cp.Problem(cp.Minimize(objkl),constrs) p_refProb.value=npSPriors result = klprob.solve() print result # # Risk return tradeoff curve # def test_risk_return_tradeoff(self): # from math import sqrt # from cvxopt import matrix # from cvxopt.blas import dot # from cvxopt.solvers import qp, options # import scipy # n = 4 # S = matrix( [[ 4e-2, 6e-3, -4e-3, 0.0 ], # [ 6e-3, 1e-2, 0.0, 0.0 ], # [-4e-3, 0.0, 2.5e-3, 0.0 ], # [ 0.0, 0.0, 0.0, 0.0 ]] ) # pbar = matrix([.12, .10, .07, .03]) # N = 100 # # CVXPY # Sroot = numpy.asmatrix(scipy.linalg.sqrtm(S)) # x = cp.Variable(n, name='x') # mu = cp.Parameter(name='mu') # mu.value = 1 # TODO cp.Parameter("positive") # objective = cp.Minimize(-pbar*x + mu*quad_over_lin(Sroot*x,1)) # constraints = [sum(x) == 1, x >= 0] # p = cp.Problem(objective, constraints) # mus = [ 10**(5.0*t/N-1.0) for t in range(N) ] # xs = [] # for mu_val in mus: # mu.value = mu_val # p.solve() # xs.append(x.value) # returns = [ dot(pbar,x) for x in xs ] # risks = [ sqrt(dot(x, S*x)) for x in xs ] # # QP solver