def pow_nd_canon(con, args): """ con : PowConeND We can extract metadata from this. For example, con.alpha and con.axis. args : tuple of length two W,z = args[0], args[1] """ alpha, axis = con.get_data() alpha = alpha.value W, z = args if axis == 1: W = W.T alpha = alpha.T if W.ndim == 1: W = reshape(W, (W.size, 1)) alpha = np.reshape(alpha, (W.size, 1)) n, k = W.shape if n == 2: can_con = PowCone3D(W[0, :], W[1, :], z, alpha[0, :]) else: T = Variable(shape=(n - 2, k)) x_3d, y_3d, z_3d, alpha_3d = [], [], [], [] for j in range(k): x_3d.append(W[:-1, j]) y_3d.append(T[:, j]) y_3d.append(W[n - 1, j]) z_3d.append(z[j]) z_3d.append(T[:, j]) r_nums = alpha[:, j] r_dens = np.cumsum(r_nums[::-1])[::-1] # ^ equivalent to [np.sum(alpha[i:, j]) for i in range(n)] r = r_nums / r_dens alpha_3d.append(r[:n - 1]) x_3d = hstack(x_3d) y_3d = hstack(y_3d) z_3d = hstack(z_3d) alpha_p3d = hstack(alpha_3d) # TODO: Ideally we should construct x,y,z,alpha_p3d by # applying suitable sparse matrices to W,z,T, rather # than using the hstack atom. (hstack will probably # result in longer compile times). can_con = PowCone3D(x_3d, y_3d, z_3d, alpha_p3d) # Return a single PowCone3D constraint defined over all auxiliary # variables needed for the reduction to go through. # There are no "auxiliary constraints" beyond this one. return can_con, []
def add_canon(expr, args): if expr.is_scalar(): return log_sum_exp(hstack(args)), [] rows = [] summands = [promote(s, expr.shape) if s.is_scalar() else s for s in args] if len(expr.shape) == 1: for i in range(expr.shape[0]): row = [] row.append( log_sum_exp(hstack([summand[i] for summand in summands]))) rows.append(row) return reshape(bmat(rows), expr.shape), [] else: for i in range(expr.shape[0]): row = [] for j in range(expr.shape[1]): row.append( log_sum_exp(hstack([summand[i, j] for summand in summands]))) rows.append(row) return reshape(bmat(rows), expr.shape), []
def quad_over_lin_canon(expr, args): # quad_over_lin := sum_{ij} X^2_{ij} / y x = args[0] y = args[1].flatten() # precondition: shape == () t = Variable(1,) # (y+t, y-t, 2*x) must lie in the second-order cone, # where y+t is the scalar part of the second-order # cone constraint. constraints = [SOC( t=y+t, X=hstack([y-t, 2*x.flatten()]), axis=0 ), y >= 0] return t, constraints
def sum_canon(expr, args): X = args[0] if expr.axis is None: summation = explicit_sum(X) canon, _ = add_canon(summation, summation.args) return reshape(canon, expr.shape), [] if expr.axis == 0: X = X.T rows = [] for i in range(X.shape[0]): summation = explicit_sum(X[i]) canon, _ = add_canon(summation, summation.args) rows.append(canon) canon = hstack(rows) return reshape(canon, expr.shape), []
def bmat(block_lists): """Constructs a block matrix. Takes a list of lists. Each internal list is stacked horizontally. The internal lists are stacked vertically. Parameters ---------- block_lists : list of lists The blocks of the block matrix. Return ------ CVXPY expression The CVXPY expression representing the block matrix. """ row_blocks = [hstack(*blocks) for blocks in block_lists] return vstack(*row_blocks)
def mulexpression_canon(expr, args): lhs = args[0] rhs = args[1] lhs_shape, rhs_shape, _ = mul_shapes_promote(lhs.shape, rhs.shape) lhs = reshape(lhs, lhs_shape) rhs = reshape(rhs, rhs_shape) rows = [] # TODO(akshayka): Parallelize this for large matrices. for i in range(lhs.shape[0]): row = [] for j in range(rhs.shape[1]): arr = hstack([lhs[i, k] + rhs[k, j] for k in range(lhs.shape[1])]) row.append(log_sum_exp(arr)) rows.append(row) mat = bmat(rows) if mat.shape != expr.shape: mat = reshape(mat, expr.shape) return mat, []
def deep_flatten(x): # base cases if isinstance(x, Expression): if len(x.shape) == 1: return x else: return x.flatten() elif isinstance(x, np.ndarray) or isinstance(x, (int, float)): x = Expression.cast_to_const(x) return x.flatten() # recursion if isinstance(x, list): y = [] for x0 in x: x1 = deep_flatten(x0) y.append(x1) y = hstack(y) return y msg = 'The input to deep_flatten must be an Expression, a NumPy array, an int'\ + ' or float, or a nested list thereof. Received input of type %s' % type(x) raise ValueError(msg)
def prod(expr, axis=None, keepdims: bool = False) -> Prod: """Multiply the entries of an expression. The semantics of this atom are the same as np.prod. This atom is log-log affine, but it is neither convex nor concave. Parameters ---------- expr : Expression or list[Expression, Numeric] The expression to multiply the entries of, or a list of Expressions and numeric types. axis : int The axis along which to take the product; ignored if `expr` is a list. keepdims : bool Whether to drop dimensions after taking the product; ignored if `expr` is a list. """ if isinstance(expr, list): return Prod(hstack(expr)) else: return Prod(expr, axis, keepdims)
def mixed_norm(X, p=2, q=1): """Lp,q norm; :math:` (\sum_k (\sum_l \lvert x_{k,l} \rvert )^q/p)^{1/q}`. Parameters ---------- X : Expression or numeric constant The matrix to take the l_{p,q} norm of. p : int or str, optional The type of inner norm. q : int or str, optional The type of outer norm. Returns ------- Expression An Expression representing the mixed norm. """ X = Expression.cast_to_const(X) # inner norms vecnorms = [norm(X[i, :], p) for i in range(X.size[0])] # outer norm return norm(hstack(*vecnorms), q)
def col_norm(X, p=2): X = Expression.cast_to_const(X) vecnorms = [norm(X[:, i], p) for i in range(X.size[1])] return hstack(*vecnorms)
def col_norm(X, p=2): X = Expression.cast_to_const(X) vecnorms = [ norm(X[:, i], p) for i in range(X.size[1]) ] return hstack(*vecnorms)
def row_norm(X, p=2): X = Expression.cast_to_const(X) vecnorms = [ norm(X[i, :], p) for i in range(X.size[0]) ] return hstack(*vecnorms).T