def apply(self, problem): """Recursively canonicalize the objective and every constraint. """ constraints = [] for constr in problem.constraints: constraints += self._canonicalize_constraint(constr) lazy, real = _get_lazy_and_real_constraints(constraints) feas_problem = problems.problem.Problem(Minimize(0), real) feas_problem._lazy_constraints = lazy objective = problem.objective.expr if objective.is_nonneg(): t = Parameter(nonneg=True) elif objective.is_nonpos(): t = Parameter(nonpos=True) else: t = Parameter() constraints += self._canonicalize_constraint(objective <= t) lazy, real = _get_lazy_and_real_constraints(constraints) param_problem = problems.problem.Problem(Minimize(0), real) param_problem._lazy_constraints = lazy param_problem._bisection_data = BisectionData( feas_problem, t, *tighten.tighten_fns(objective)) return param_problem, InverseData(problem)
def DCCP_ini(self): dom_constr = self.objective.args[0].domain for arg in self.constraints: for dom in arg.args[0].domain: dom_constr.append(dom) for dom in arg.args[1].domain: dom_constr.append(dom) var_store = [] init_flag = [] for var in self.variables(): var_store.append(np.zeros((var._rows,var._cols))) init_flag.append(var.value is None) times = 3 for t in range(times): ini_cost = 0 var_ind = 0 for var in self.variables(): if init_flag[var_ind]: var.value = np.random.randn(var._rows,var._cols)*10 ini_cost += pnorm(var-var.value,2) var_ind += 1 ini_obj = Minimize(ini_cost) ini_prob = Problem(ini_obj,dom_constr) ini_prob.solve() var_ind = 0 for var in self.variables(): var_store[var_ind] = var_store[var_ind] + var.value/float(times) var_ind += 1 var_ind = 0 for var in self.variables(): var.value = var_store[var_ind] var_ind += 1
def _lower_problem(problem): """Evaluates lazy constraints.""" constrs = [c() if callable(c) else c for c in problem.constraints] constrs = [c for c in constrs if c is not None] if s.INFEASIBLE in constrs: # Indicates that the problem is infeasible. return None return problems.problem.Problem(Minimize(0), constrs)
def apply(self, problem): inverse_data = InverseData(problem) is_minimize = type(problem.objective) == Minimize chance_expr, chance_constraints = self.chance_tree(problem.objective.args[0], is_minimize, True, False) chance_objective = Minimize(chance_expr) if is_minimize else Maximize(chance_expr) if any([type(atom) == cc.quantile for con in problem.constraints for atom in con.atoms()]): raise DCPError("Quantile atom may not be nested in constraints.") new_problem = cc.problem.Problem(chance_objective, problem.constraints + chance_constraints) return new_problem, inverse_data
def targets_and_priorities( objectives: List[Union[Minimize, Maximize]], priorities, targets, limits=None, off_target: float = 1e-5) -> Union[Minimize, Maximize]: """Combines objectives with penalties within a range between target and limit. Each Minimize objective i has value priorities[i]*objectives[i] when objectives[i] >= targets[i] +infinity when objectives[i] > limits[i] Each Maximize objective i has value priorities[i]*objectives[i] when objectives[i] <= targets[i] +infinity when objectives[i] < limits[i] Args: objectives: A list of Minimize/Maximize objectives. priorities: The weight within the trange. targets: The start (end) of penalty for Minimize (Maximize) limits: The hard end (start) of penalty for Minimize (Maximize) off_target: Penalty outside of target. Returns: A Minimize/Maximize objective. """ num_objs = len(objectives) new_objs: List[Union[Minimize, Maximize]] = [] for i in range(num_objs): obj = objectives[i] sign = 1 if Constant.cast_to_const(priorities[i]).is_nonneg() else -1 off_target *= sign if type(obj) == Minimize: expr = (priorities[i] - off_target) * atoms.pos(obj.args[0] - targets[i]) expr += off_target * obj.args[0] if limits is not None: expr += sign * indicator([obj.args[0] <= limits[i]]) new_objs.append(expr) else: # Maximize expr = (priorities[i] - off_target) * atoms.min_elemwise( obj.args[0], targets[i]) expr += off_target * obj.args[0] if limits is not None: expr += sign * indicator([obj.args[0] >= limits[i]]) new_objs.append(expr) obj_expr = sum(new_objs) if obj_expr.is_convex(): return Minimize(obj_expr) else: return Maximize(obj_expr)
def max(objectives, weights): """Combines objectives as max of weighted terms. Args: objectives: A list of Minimize/Maximize objectives. weights: A vector of weights. Returns: A Minimize objective. """ num_objs = len(objectives) expr = atoms.max_elemwise([(objectives[i]*weights[i]).args[0] for i in range(num_objs)]) return Minimize(expr)
def _get_Theta(self, U, F): """ Function to find Theta from U and F via convex optimization in cvxpy or euclidean_proj_simplex Parameters --------- U: array-like with shape (n_nodes, n_clusters) F: nd.array with shape (n_clusters, n_clusters) with coordinates of k pretenders to be pure nodes Returns ------- Theta: nd.array with shape (n_nodes, n_clusters) where n_nodes == U.shape[0], n_clusters == U.shape[1] Requires -------- cvxpy (http://www.cvxpy.org/en/latest/) """ assert U.shape[1] == F.shape[0] == F.shape[1], \ "U.shape[1] != F.shape" n_nodes = U.shape[0] n_clusters = U.shape[1] if self.use_cvxpy: Theta = Variable(rows=n_nodes, cols=n_clusters) constraints = [ sum_entries(Theta[i, :]) == 1 for i in range(n_nodes) ] constraints += [ Theta[i, j] >= 0 for i in range(n_nodes) for j in range(n_clusters) ] obj = Minimize(norm(U - Theta * F, 'fro')) prob = Problem(obj, constraints) prob.solve() return np.array(Theta.value) else: projector = F.T.dot(np.linalg.inv(F.dot(F.T))) theta = U.dot(projector) theta_simplex_proj = np.array([ self._euclidean_proj_simplex(x) for x in theta ]) return theta_simplex_proj
def extract_quadratic_coeffs(self, affine_expr, quad_forms): """ Assumes quadratic forms all have variable arguments. Affine expressions can be anything. """ # Extract affine data. affine_problem = cvxpy.Problem(Minimize(affine_expr), []) affine_inverse_data = InverseData(affine_problem) affine_id_map = affine_inverse_data.id_map affine_var_shapes = affine_inverse_data.var_shapes extractor = CoeffExtractor(affine_inverse_data) c, b = extractor.affine(affine_problem.objective.expr) # Combine affine data with quadforms. coeffs = {} for var in affine_problem.variables(): if var.id in quad_forms: var_id = var.id orig_id = quad_forms[var_id][2].args[0].id var_offset = affine_id_map[var_id][0] var_size = affine_id_map[var_id][1] if quad_forms[var_id][2].P.value is not None: c_part = c[0, var_offset:var_offset + var_size].toarray().flatten() P = quad_forms[var_id][2].P.value if sp.issparse(P): P = P.toarray() P = c_part * P else: P = sp.diags(c[0, var_offset:var_offset + var_size].toarray().flatten()) if orig_id in coeffs: coeffs[orig_id]['P'] += P coeffs[orig_id]['q'] += np.zeros(P.shape[0]) else: coeffs[orig_id] = dict() coeffs[orig_id]['P'] = P coeffs[orig_id]['q'] = np.zeros(P.shape[0]) else: var_offset = affine_id_map[var.id][0] var_size = np.prod(affine_var_shapes[var.id], dtype=int) if var.id in coeffs: coeffs[var.id]['P'] += sp.csr_matrix((var_size, var_size)) coeffs[var.id]['q'] += c[0, var_offset:var_offset + var_size].toarray().flatten() else: coeffs[var.id] = dict() coeffs[var.id]['P'] = sp.csr_matrix((var_size, var_size)) coeffs[var.id]['q'] = c[0, var_offset:var_offset + var_size].toarray().flatten() return coeffs, b
def max(objectives: List[Union[Minimize, Maximize]], weights) -> Minimize: """Combines objectives as max of weighted terms. Args: objectives: A list of Minimize/Maximize objectives. weights: A vector of weights. Returns: A Minimize objective. """ num_objs = len(objectives) expr = atoms.maximum(*[(objectives[i] * weights[i]).args[0] for i in range(num_objs)]) return Minimize(expr)
def log_sum_exp(objectives, weights, gamma): """Combines objectives as log_sum_exp of weighted terms. Args: objectives: A list of Minimize/Maximize objectives. weights: A vector of weights. gamma: Parameter interpolating between sum and max. Returns: A Minimize objective. """ num_objs = len(objectives) terms = [(objectives[i]*weights[i]).args[0] for i in range(num_objs)] expr = atoms.log_sum_exp(atoms.vstack(terms)) return Minimize(expr)
def stuffed_objective(self, problem, inverse_data): # We need to copy the problem because we are changing atoms in the # expression tree problem_copy = problems.problem.Problem( Minimize(problem.objective.expr.tree_copy()), [con.tree_copy() for con in problem.constraints]) inverse_data_of_copy = InverseData(problem_copy) extractor = CoeffExtractor(inverse_data_of_copy) # extract to x.T * P * x + q.T * x, store r P, q, r = extractor.quad_form(problem_copy.objective.expr) # concatenate all variables in one vector boolean, integer = extract_mip_idx(problem.variables()) x = Variable(inverse_data.x_length, boolean=boolean, integer=integer) new_obj = QuadForm(x, P) + q.T * x inverse_data.r = r return new_obj, x
def apply(self, problem): inverse_data = InverseData(problem) new_obj, new_var = self.stuffed_objective(problem, inverse_data) # Form the constraints extractor = CoeffExtractor(inverse_data) new_cons = [] for con in problem.constraints: arg_list = [] for arg in con.args: A, b = extractor.get_coeffs(arg) arg_list.append(reshape(A * new_var + b, arg.shape)) new_cons.append(con.copy(arg_list)) inverse_data.cons_id_map[con.id] = new_cons[-1].id # Map of old constraint id to new constraint id. inverse_data.minimize = type(problem.objective) == Minimize new_prob = problems.problem.Problem(Minimize(new_obj), new_cons) return new_prob, inverse_data
def _get_Q(self, U): """ Get positive semidefinite Q matrix of ellipsoid from U[i,:] vectors for any u_i in U[i,:] : abs(u_i.T * Q * u_i) <= 1 Parameters --------- U: array-like Returns ------- Q: nd.array with shape (U.shape[1], U.shape[1]) Requires: --------- cvxpy (http://www.cvxpy.org/en/latest/) scipy.spatial.ConvexHull """ n_nodes = U.shape[0] k = U.shape[1] Q = Semidef(n=k) if self.use_convex_hull: hull = ConvexHull(U) constraints = [ abs(U[i, :].reshape((1, k)) * Q * U[i, :].reshape((k, 1))) \ <= 1 for i in hull.vertices ] else: constraints = [ abs(U[i, :].reshape((1, k)) * Q * U[i, :].reshape((k, 1))) \ <= 1 for i in range(n_nodes) ] obj = Minimize(-log_det(Q)) prob = Problem(obj, constraints) _ = prob.solve(solver=self.solver) Q = np.array(Q.value) return Q
def apply(self, problem): """See docstring for MatrixStuffing.apply""" inverse_data = InverseData(problem) # Form the constraints extractor = CoeffExtractor(inverse_data) new_obj, new_var, r = self.stuffed_objective(problem, extractor) inverse_data.r = r # Lower equality and inequality to Zero and NonPos. cons = [] for con in problem.constraints: if isinstance(con, Equality): con = lower_equality(con) elif isinstance(con, Inequality): con = lower_inequality(con) cons.append(con) # Batch expressions together, then split apart. expr_list = [arg for c in cons for arg in c.args] problem_data_tensor = extractor.affine(expr_list) Afull, bfull = canon.get_matrix_and_offset_from_unparameterized_tensor( problem_data_tensor, new_var.size) if 0 not in Afull.shape and 0 not in bfull.shape: Afull = cvxtypes.constant()(Afull) bfull = cvxtypes.constant()(np.atleast_1d(bfull)) new_cons = [] offset = 0 for orig_con, con in zip(problem.constraints, cons): arg_list = [] for arg in con.args: A = Afull[offset:offset + arg.size, :] b = bfull[offset:offset + arg.size] arg_list.append(reshape(A @ new_var + b, arg.shape)) offset += arg.size new_constraint = con.copy(arg_list) new_cons.append(new_constraint) inverse_data.constraints = new_cons inverse_data.minimize = type(problem.objective) == Minimize new_prob = problems.problem.Problem(Minimize(new_obj), new_cons) return new_prob, inverse_data
def log_sum_exp(objectives, weights, gamma=1): """Combines objectives as log_sum_exp of weighted terms. The objective takes the form log(sum_{i=1}^n exp(gamma*weights[i]*objectives[i]))/gamma As gamma goes to 0, log_sum_exp approaches weighted_sum. As gamma goes to infinity, log_sum_exp approaches max. Args: objectives: A list of Minimize/Maximize objectives. weights: A vector of weights. gamma: Parameter interpolating between weighted_sum and max. Returns: A Minimize objective. """ num_objs = len(objectives) terms = [(objectives[i] * weights[i]).args[0] for i in range(num_objs)] expr = atoms.log_sum_exp(gamma * atoms.vstack(terms)) / gamma return Minimize(expr)
def scs_coniclift(x, constraints): """ Return (A, b, K) so that {x : x satisfies constraints} can be written as {x : exists y where A @ [x; y] + b in K}. Parameters ---------- x: cvxpy.Variable constraints: list of cvxpy.constraints.constraint.Constraint Each Constraint object must be DCP-compatible. Notes ----- This function DOES NOT work when ``x`` has attributes, like ``PSD=True``, ``diag=True``, ``symmetric=True``, etc... """ from cvxpy.problems.problem import Problem from cvxpy.problems.objective import Minimize from cvxpy.atoms.affine.sum import sum prob = Problem(Minimize(sum(x)), constraints) # ^ The objective value is only used to make sure that "x" # participates in the problem. So, if constraints is an # empty list, then the support function is the standard # support function for R^n. data, chain, invdata = prob.get_problem_data(solver='SCS') inv = invdata[-2] x_offset = inv.var_offsets[x.id] x_indices = np.arange(x_offset, x_offset + x.size) A = data['A'] x_selector = np.zeros(shape=(A.shape[1], ), dtype=bool) x_selector[x_indices] = True A_x = A[:, x_selector] A_other = A[:, ~x_selector] A = -sparse.hstack([A_x, A_other]) b = data['b'] K = data['dims'] return A, b, K
def apply(self, problem): inverse_data = InverseData(problem) # Form the constraints extractor = CoeffExtractor(inverse_data) new_obj, new_var, r = self.stuffed_objective(problem, extractor) inverse_data.r = r # Lower equality and inequality to Zero and NonPos. cons = [] for con in problem.constraints: if isinstance(con, Equality): con = lower_equality(con) elif isinstance(con, Inequality): con = lower_inequality(con) elif isinstance(con, SOC) and con.axis == 1: con = SOC(con.args[0], con.args[1].T, axis=0, constr_id=con.constr_id) cons.append(con) # Batch expressions together, then split apart. expr_list = [arg for con in cons for arg in con.args] Afull, bfull = extractor.affine(expr_list) new_cons = [] offset = 0 for con in cons: arg_list = [] for arg in con.args: A = Afull[offset:offset + arg.size, :] b = bfull[offset:offset + arg.size] arg_list.append(reshape(A * new_var + b, arg.shape)) offset += arg.size new_cons.append(con.copy(arg_list)) inverse_data.cons_id_map[con.id] = new_cons[-1].id # Map of old constraint id to new constraint id. inverse_data.minimize = type(problem.objective) == Minimize new_prob = problems.problem.Problem(Minimize(new_obj), new_cons) return new_prob, inverse_data
def targets_and_priorities(objectives, priorities, targets, limits=None, off_target=1e-5): """Combines objectives with penalties within a range between target and limit. Args: objectives: A list of Minimize/Maximize objectives. priorities: The weight within the trange. targets: The start (end) of penalty for Minimize (Maximize) limits: The hard end (start) of penalty for Minimize (Maximize) off_target: Penalty outside of target. Returns: A Minimize/Maximize objective. """ num_objs = len(objectives) new_objs = [] for i in range(num_objs): obj = objectives[i] sign = 1 if Constant(priorities[i]).is_positive() else -1 off_target *= sign if type(obj) == Minimize: expr = (priorities[i] - off_target)*atoms.pos(obj.args[0] - targets[i]) expr += off_target*obj.args[0] if limits is not None: expr += sign*indicator([obj.args[0] <= limits[i]]) new_objs.append(expr) else: # Maximize expr = (priorities[i] - off_target)*atoms.min_elemwise(obj.args[0], targets[i]) expr += off_target*obj.args[0] if limits is not None: expr += sign*indicator([obj.args[0] >= limits[i]]) new_objs.append(expr) obj_expr = sum(new_objs) if obj_expr.is_convex(): return Minimize(obj_expr) else: return Maximize(obj_expr)
def _lower_problem(problem): """Evaluates lazy constraints.""" return problems.problem.Problem( Minimize(0), problem.constraints + [c() for c in problem._lazy_constraints])
def apply(self, problem): is_maximize = type(problem.objective) == Maximize if is_maximize: problem = cvxtypes.problem()(Minimize(-problem.objective.expr), problem.constraints) return problem, is_maximize
def iter_DCCP(self,solver,max_iter, tau, miu, tau_max): it = 1 # keep the values from the previous iteration or initialization previous_cost = float("inf") variable_pres_value = [] for var in self.variables(): variable_pres_value.append(var.value) # each non-dcp constraint needs a slack variable var_slack = [] for constr in self.constraints: if not constr.is_dcp(): rows, cols = constr.size var_slack.append(Variable(rows, cols)) while it<=max_iter and all(var.value is not None for var in self.variables()): constr_new = [] #cost functions if not self.objective.is_dcp(): temp = self.objective.convexify() flag = temp[2] flag_var = temp[3] while flag == 1: for var in flag_var: var_index = self.variables().index(var) var.value = 0.8*var.value + 0.2* variable_pres_value[var_index] temp = self.objective.convexify() flag = temp[2] flag_var = temp[3] cost_new = temp[0] # new cost function for dom in temp[1]: # domain constraints constr_new.append(dom) else: cost_new = self.objective.args[0] #constraints count_slack = 0 for arg in self.constraints: if not arg.is_dcp(): temp = arg.convexify() flag = temp[2] flag_var = temp[3] while flag == 1: for var in flag_var: var_index = self.variables().index(var) var.value = 0.8*var.value + 0.2* variable_pres_value[var_index] temp = arg.convexify() flag = temp[2] flag_var = temp[3] newcon = temp[0] #new constraint without slack variable for dom in temp[1]:#domain constr_new.append(dom) right = newcon.args[1] + var_slack[count_slack] constr_new.append(newcon.args[0]<=right) constr_new.append(var_slack[count_slack]>=0) count_slack = count_slack+1 else: constr_new.append(arg) #objective if self.objective.NAME == 'minimize': for var in var_slack: cost_new += np.ones((var._cols,var._rows))*var*tau obj_new = Minimize(cost_new) else: for var in var_slack: cost_new -= var*tau obj_new = Maximize(cost_new) #new problem prob_new = Problem(obj_new, constr_new) variable_pres_value = [] for var in self.variables(): variable_pres_value.append(var.value) if not var_slack == []: if solver is None: print "iteration=",it, "cost value = ", prob_new.solve(), "tau = ", tau else: print "iteration=",it, "cost value = ", prob_new.solve(solver = solver), "tau = ", tau max_slack = [] for i in range(len(var_slack)): max_slack.append(np.max(var_slack[i].value)) max_slack = np.max(max_slack) print "max slack = ", max_slack else: if solver is None: co = prob_new.solve() else: co = prob_new.solve(solver = solver) print "iteration=",it, "cost value = ", co , "tau = ", tau if np.abs(previous_cost - prob_new.value) <= 1e-4: #terminate it_real = it it = max_iter+1 else: previous_cost = prob_new.value it_real = it tau = min([tau*miu,tau_max]) it += 1 if not var_slack == []: return(it_real, tau, max(var_slack[i].value for i in range(len(var_slack)))) else: return(it_real, tau)
def apply(self, problem): """Returns a stuffed problem. The returned problem is a minimization problem in which every constraint in the problem has affine arguments that are expressed in the form A @ x + b. Parameters ---------- problem: The problem to stuff; the arguments of every constraint must be affine constraints: A list of constraints, whose arguments are affine Returns ------- Problem The stuffed problem InverseData Data for solution retrieval """ inverse_data = InverseData(problem) # Form the constraints extractor = CoeffExtractor(inverse_data) new_obj, new_var, r = self.stuffed_objective(problem, extractor) inverse_data.r = r # Lower equality and inequality to Zero and NonPos. cons = [] for con in problem.constraints: if isinstance(con, Equality): con = lower_equality(con) elif isinstance(con, Inequality): con = lower_inequality(con) elif isinstance(con, SOC) and con.axis == 1: con = SOC(con.args[0], con.args[1].T, axis=0, constr_id=con.constr_id) cons.append(con) # Batch expressions together, then split apart. expr_list = [arg for c in cons for arg in c.args] Afull, bfull = extractor.affine(expr_list) if 0 not in Afull.shape and 0 not in bfull.shape: Afull = cvxtypes.constant()(Afull) bfull = cvxtypes.constant()(bfull) new_cons = [] offset = 0 for con in cons: arg_list = [] for arg in con.args: A = Afull[offset:offset+arg.size, :] b = bfull[offset:offset+arg.size] arg_list.append(reshape(A*new_var + b, arg.shape)) offset += arg.size new_cons.append(con.copy(arg_list)) # Map old constraint id to new constraint id. inverse_data.cons_id_map[con.id] = new_cons[-1].id inverse_data.minimize = type(problem.objective) == Minimize new_prob = problems.problem.Problem(Minimize(new_obj), new_cons) return new_prob, inverse_data
def setUp(self): self.x = Variable(2, name='x') self.Q = np.eye(2) self.c = np.array([1, 0.5]) self.qp = Problem(Minimize(QuadForm(self.x, self.Q)), [self.x <= -1]) self.cp = Problem(Minimize(self.c.T * self.x + 1), [self.x >= 0])