def epigraph_conic_form(self): """ Refer to coniclifts/standards/cone_standards.txt to see that "(x, y) : e^x <= y" is represented as "(x, y, 1) \in K_{exp}". :return: """ b = np.zeros(3, ) K = [Cone('e', 3)] A_rows, A_cols, A_vals = [], [], [] x = self.args[0] # first coordinate num_nonconst = len(x) - 1 if num_nonconst > 0: A_rows += num_nonconst * [0] A_cols = [var.id for var, co in x[:-1]] A_vals = [co for var, co in x[:-1]] else: # infer correct dimensions later on A_rows.append(0) A_cols.append(ScalarVariable.curr_variable_count() - 1) A_vals.append(0) b[0] = x[-1][1] # second coordinate A_rows.append(1), A_cols.append(self._epigraph_variable.id) A_vals.append(1) # third coordinate (zeros for A, but included to infer correct dims later on) A_rows.append(2) A_cols.append(ScalarVariable.curr_variable_count() - 1) A_vals.append(0) b[2] = 1 return A_vals, np.array(A_rows), A_cols, b, K
def conic_form(self): """ Returns a sparse matrix representation of the Power cone object. It represents of the object as :math:'Ax+b \\in K' where K is a cone object Returns ------- A_vals - list (of floats) A_rows - numpy 1darray (of integers) A_cols - list (of integers) b - numpy 1darray (of floats) K - list (of coniclifts Cone objects) """ A_rows, A_cols, A_vals = [], [], [] K = [ Cone('pow', self.w.size, annotations={'weights': self.alpha.tolist()}) ] b = np.zeros(shape=(self.w.size, )) # Loop through w and add every variable for i, se in enumerate(self.w_low.flat): if len(se.atoms_to_coeffs) == 0: b[i] = se.offset A_rows.append(i) A_cols.append(ScalarVariable.curr_variable_count() - 1) # make sure scipy infers correct dimensions later on. A_vals.append(0) else: b[i] = se.offset A_rows += [i] * len(se.atoms_to_coeffs) col_idx_to_coeff = [(a.id, c) for a, c in se.atoms_to_coeffs.items()] A_cols += [atom_id for (atom_id, _) in col_idx_to_coeff] A_vals += [c for (_, c) in col_idx_to_coeff] # Make final row of matrix the information about z i = self.w_low.size # Loop through w and add every variable for se in self.z_low.flat: if len(se.atoms_to_coeffs) == 0: b[i] = se.offset A_rows.append(i) A_cols.append(ScalarVariable.curr_variable_count() - 1) # make sure scipy infers correct dimensions later on. A_vals.append(0) else: b[i] = se.offset A_rows += [i] * len(se.atoms_to_coeffs) col_idx_to_coeff = [(a.id, c) for a, c in se.atoms_to_coeffs.items()] A_cols += [atom_id for (atom_id, _) in col_idx_to_coeff] A_vals += [c for (_, c) in col_idx_to_coeff] return [(A_vals, np.array(A_rows), A_cols, b, K)]
def epigraph_conic_form(self): """ Generate conic constraint for epigraph np.linalg.norm( np.array(self.args), ord=2) <= self._epigraph_variable The coniclifts standard for the second order cone (of length n) is { (t,x) : x \in R^{n-1}, t \in R, || x ||_2 <= t }. """ m = len(self.args) + 1 b = np.zeros(m, ) A_rows, A_cols, A_vals = [0], [self._epigraph_variable.id ], [1] # for first row for i, arg in enumerate(self.args): nonconst_terms = len(arg) - 1 if nonconst_terms > 0: A_rows += nonconst_terms * [i + 1] for var, coeff in arg[:-1]: A_cols.append(var.id) A_vals.append(coeff) else: # make sure we infer correct dimensions later on A_rows.append(i + 1) A_cols.append(ScalarVariable.curr_variable_count() - 1) A_vals.append(0) b[i + 1] = arg[-1][1] K = [Cone('S', m)] A_rows = np.array(A_rows) return A_vals, A_rows, A_cols, b, K
def columns_sum_leq_vec(mat, vec, mat_offsets=False): # This function assumes that each ScalarExpression in mat # consists of a single ScalarVariable with coefficient one, # and has no offset term. A_rows, A_cols, A_vals = [], [], [] m = mat.shape[0] if m != vec.size: raise RuntimeError('Incompatible dimensions.') b = np.zeros(m, ) for i in range(m): # update cols and data to reflect addition of elements in ith row of mat svs = mat[i, :].scalar_variables() A_cols += [sv.id for sv in svs] A_vals += [-1] * len(svs) # update cols and data to reflect addition of elements from ith element of vec # ith element of vec is a ScalarExpression! id2co = [(a.id, co) for a, co in vec[i].atoms_to_coeffs.items()] A_cols += [aid for aid, _ in id2co] A_vals += [co for _, co in id2co] # update rows with appropriate number of "i"s. total_len = len(svs) + len(id2co) if total_len > 0: A_rows += [i] * total_len else: A_rows.append(i) A_vals.append(0) A_cols.append(ScalarVariable.curr_variable_count() - 1) # update b b[i] = vec[i].offset if mat_offsets: b[i] -= sum([matij.offset for matij in mat[i, :]]) A_rows = np.array(A_rows) return A_vals, A_rows, A_cols, b
def epigraph_conic_form(self): """ Generate conic constraint for epigraph self.args[0] * ln( self.args[0] / self.args[1] ) <= self._epigraph_variable. :return: """ b = np.zeros(3,) K = [Cone('e', 3)] # ^ initializations A_rows, A_cols, A_vals = [0], [self._epigraph_variable.id], [-1] # ^ first row x = self.args[0] num_nonconst = len(x) - 1 if num_nonconst > 0: A_rows += num_nonconst * [2] A_cols += [var.id for var, co in x[:-1]] A_vals += [co for var, co in x[:-1]] else: A_rows.append(2) A_cols.append(ScalarVariable.curr_variable_count() - 1) A_vals.append(0) b[2] = x[-1][1] # ^ third row y = self.args[1] num_nonconst = len(y) - 1 if num_nonconst > 0: A_rows += num_nonconst * [1] A_cols += [var.id for var, co in y[:-1]] A_vals += [co for var, co in y[:-1]] else: A_rows.append(1) A_cols.append(ScalarVariable.curr_variable_count() - 1) A_vals.append(0) b[1] = y[-1][1] # ^ second row return A_vals, np.array(A_rows), A_cols, b, K
def conic_form(self): A_rows, A_cols, A_vals = [], [], [] b = np.zeros(shape=(self.K_size,)) for i, se in enumerate(self.y.flat): if len(se.atoms_to_coeffs) == 0: b[i] = se.offset A_rows.append(i) A_cols.append(ScalarVariable.curr_variable_count() - 1) A_vals.append(0) # make sure scipy infers correct dimensions later on. else: b[i] = se.offset A_rows += [i] * len(se.atoms_to_coeffs) col_idx_to_coeff = [(a.id, c) for a, c in se.atoms_to_coeffs.items()] A_cols += [atom_id for (atom_id, _) in col_idx_to_coeff] A_vals += [c for (_, c) in col_idx_to_coeff] return [(A_vals, np.array(A_rows), A_cols, b, self.K)]
def conic_form(self): self_dual_cones = {'+', 'S', 'P'} start_row = 0 y_mod = [] for co in self.K: stop_row = start_row + co.len if co.type in self_dual_cones: y_mod.append(self.y[start_row:stop_row]) elif co.type == 'e': temp_y = np.array([ -self.y[start_row + 2], np.exp(1) * self.y[start_row + 1], -self.y[start_row] ]) y_mod.append(temp_y) elif co.type != '0': raise RuntimeError('Unexpected cone type "%s".' % str(co.type)) start_row = stop_row y_mod = np.hstack(y_mod) y_mod = Expression(y_mod) # Now we can pretend all nonzero cones are self-dual. A_vals, A_rows, A_cols = [], [], [] cur_K = [Cone(co.type, co.len) for co in self.K if co.type != '0'] cur_K_size = sum([co.len for co in cur_K]) if cur_K_size > 0: b = np.zeros(shape=(cur_K_size, )) for i, se in enumerate(y_mod): if len(se.atoms_to_coeffs) == 0: b[i] = se.offset A_rows.append(i) A_cols.append( int(ScalarVariable.curr_variable_count()) - 1) A_vals.append( 0 ) # make sure scipy infers correct dimensions later on. else: b[i] = se.offset A_rows += [i] * len(se.atoms_to_coeffs) col_idx_to_coeff = [(a.id, c) for a, c in se.atoms_to_coeffs.items()] A_cols += [atom_id for (atom_id, _) in col_idx_to_coeff] A_vals += [c for (_, c) in col_idx_to_coeff] return [(A_vals, np.array(A_rows), A_cols, b, cur_K)] else: return [([], np.zeros(shape=(0, ), dtype=int), [], np.zeros(shape=(0, )), [])]
def conic_form(self): from sageopt.coniclifts.base import Expression, ScalarVariable expr = np.triu(self.arg).view(Expression) expr = expr[np.triu_indices(expr.shape[0])] K = [Cone('P', expr.size)] b = np.empty(shape=(expr.size, )) A_rows, A_cols, A_vals = [], [], [] for i, se in enumerate(expr): b[i] = se.offset if len(se.atoms_to_coeffs) == 0: A_rows.append(i) A_cols.append(ScalarVariable.curr_variable_count()) A_vals.append( 0) # make sure scipy infers correct dimensions later on. else: A_rows += [i] * len(se.atoms_to_coeffs) cols_and_coeff = [(a.id, c) for a, c in se.atoms_to_coeffs.items()] A_cols += [atom_id for (atom_id, _) in cols_and_coeff] A_vals += [c for (_, c) in cols_and_coeff] return [(A_vals, np.array(A_rows), A_cols, b, K)]
def conic_form(self): from sageopt.coniclifts.base import ScalarVariable # This function assumes that self.expr is affine (i.e. that any necessary epigraph # variables have been substituted into the nonlinear expression). # # The vector "K" returned by this function may only include entries for # the zero cone and R_+. # # Note: signs on coefficients are inverted in this function. This happens # because flipping signs on A and b won't affect the zero cone, and # it correctly converts affine constraints of the form "expression <= 0" # to the form "-expression >= 0". We want this latter form because our # primitive cones are the zero cone and R_+. if not self.epigraph_checked: raise RuntimeError( 'Cannot canonicalize without check for epigraph substitution.') m = self.expr.size b = np.empty(shape=(m, )) if self.operator == '==': K = [Cone('0', m)] elif self.operator == '<=': K = [Cone('+', m)] else: raise RuntimeError('Unknown operator.') A_rows, A_cols, A_vals = [], [], [] for i, se in enumerate(self.expr.flat): if len(se.atoms_to_coeffs) == 0: b[i] = -se.offset A_rows.append(i) A_cols.append(int(ScalarVariable.curr_variable_count()) - 1) A_vals.append( 0) # make sure scipy infers correct dimensions later on. else: b[i] = -se.offset A_rows += [i] * len(se.atoms_to_coeffs) col_idx_to_coeff = [(a.id, c) for a, c in se.atoms_to_coeffs.items()] A_cols += [atom_id for (atom_id, _) in col_idx_to_coeff] A_vals += [-c for (_, c) in col_idx_to_coeff] return A_vals, np.array(A_rows), A_cols, b, K