def diff(x, k=1): """ Vector of kth order differences. Takes in a vector of length n and returns a vector of length n-k of the kth order differences. diff(x) returns the vector of differences between adjacent elements in the vector, that is [x[2] - x[1], x[3] - x[2], ...] diff(x, 2) is the second-order differences vector, equivalently diff(diff(x)) diff(x, 0) returns the vector x unchanged """ x = Expression.cast_to_const(x) m, n = x.size try: assert 0 <= k < m assert n == 1 except: raise ValueError('Must have k >= 0 and x must be a 1D vector with < k elements') d = x for i in range(k): d = d[1:] - d[:-1] return d
def norm(x, p=2): """Wrapper on the different norm atoms. Parameters ---------- x : Expression or numeric constant The value to take the norm of. p : int or str, optional The type of norm. Returns ------- Expression An Expression representing the norm. """ x = Expression.cast_to_const(x) if p == 1: return norm1(x) elif p == "inf": return normInf(x) elif p == "nuc": return normNuc(x) elif p == "fro": return norm2(x) elif p == 2: if x.is_matrix(): return sigma_max(x) else: return norm2(x) else: raise Exception("Invalid value %s for p." % p)
def log_normcdf(x): # noqa: E501 """Elementwise log of the cumulative distribution function of a standard normal random variable. The implementation is a quadratic approximation with modest accuracy over [-4, 4]. For details on the nature of the approximation, refer to `CVXPY GitHub PR #1224 <https://github.com/cvxpy/cvxpy/pull/1224#issue-793221374>`_. .. note:: SciPy's analog of ``log_normcdf`` is called `log_ndtr <https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.log_ndtr.html>`_. We opted not to use that name because its meaning would not be obvious to the casual user. """ A = scipy.sparse.diags( np.sqrt( [ 0.02301291, 0.08070214, 0.16411522, 0.09003495, 0.08200854, 0.01371543, 0.04641081, ] ) ) b = np.array([[3.0, 2.0, 1.0, 0.0, -1.0, -2.5, -3.5]]).reshape(-1, 1) x = Expression.cast_to_const(x) flat_x = reshape(x, (1, x.size)) y = A @ (b @ np.ones(flat_x.shape) - np.ones(b.shape) @ flat_x) out = -sum_(maximum(y, 0) ** 2, axis=0) return reshape(out, x.shape)
def diff(x, k=1, axis=0): """Vector of kth order differences. Takes in a vector of length n and returns a vector of length n-k of the kth order differences. diff(x) returns the vector of differences between adjacent elements in the vector, that is [x[2] - x[1], x[3] - x[2], ...] diff(x, 2) is the second-order differences vector, equivalently diff(diff(x)) diff(x, 0) returns the vector x unchanged """ x = Expression.cast_to_const(x) if (axis == 1 and x.ndim < 2) or x.ndim == 0: raise ValueError("Invalid axis given input dimensions.") elif axis == 0: x = x.T if k < 0 or k >= x.shape[axis]: raise ValueError("Must have k >= 0 and X must have < k elements along " "axis") for i in range(k): if x.ndim == 2: x = x[1:, :] - x[:-1, :] else: x = x[1:] - x[:-1] return x.T if axis == 1 else x
def vec_to_upper_tri(expr, strict=False): expr = Expression.cast_to_const(expr) ell = expr.shape[0] if strict: # n * (n-1)/2 == ell n = ((8 * ell + 1)**0.5 + 1) // 2 else: # n * (n+1)/2 == ell n = ((8 * ell + 1)**0.5 - 1) // 2 n = int(n) # form a matrix P, of shape (n**2, ell). # the i-th block of n rows of P gives the entries of the i-th row # of the upper-triangular matrix associated with expr. # compute expr2 = P @ expr # compute expr3 = reshape(expr2, shape=(n, n)).T # expr3 is the matrix formed by reading length-n blocks of expr2, # and letting each block form a row of expr3. P_rows = [] P_row = 0 for mat_row in range(n): entries_in_row = n - mat_row if strict: entries_in_row -= 1 P_row += n - entries_in_row # these are zeros P_rows.extend(range(P_row, P_row + entries_in_row)) P_row += entries_in_row P_cols = np.arange(ell) P_vals = np.ones(P_cols.size) P = csc_matrix((P_vals, (P_rows, P_cols)), shape=(n**2, ell)) expr2 = P @ expr expr3 = reshape(expr2, (n, n)).T return expr3
def norm(x, p=2, axis=None): """Wrapper on the different norm atoms. Parameters ---------- x : Expression or numeric constant The value to take the norm of. p : int or str, optional The type of norm. Returns ------- Expression An Expression representing the norm. """ x = Expression.cast_to_const(x) # Norms for scalars same as absolute value. if p == 1 or x.is_scalar(): return pnorm(x, 1, axis) elif p == "inf": return pnorm(x, 'inf', axis) elif p == "nuc": return normNuc(x) elif p == "fro": return pnorm(x, 2, axis) elif p == 2: if axis is None and x.is_matrix(): return sigma_max(x) else: return pnorm(x, 2, axis) else: return pnorm(x, p, axis)
def diff(x, k=1, axis=0): """ Vector of kth order differences. Takes in a vector of length n and returns a vector of length n-k of the kth order differences. diff(x) returns the vector of differences between adjacent elements in the vector, that is [x[2] - x[1], x[3] - x[2], ...] diff(x, 2) is the second-order differences vector, equivalently diff(diff(x)) diff(x, 0) returns the vector x unchanged """ x = Expression.cast_to_const(x) if axis == 1: x = x.T m, n = x.size if k < 0 or k >= m: raise ValueError( 'Must have k >= 0 and X must have < k elements along axis') d = x for i in range(k): d = d[1:, :] - d[:-1, :] if axis == 1: return d.T else: return d
def tv(value): """Total variation of a vector or matrix. Uses L1 norm of discrete gradients for vectors and L2 norm of discrete gradients for matrices. Parameters ---------- value : Expression or numeric constant The value to take the total variation of. Returns ------- Expression An Expression representing the total variation. """ value = Expression.cast_to_const(value) rows, cols = value.size if value.is_scalar(): raise ValueError("tv cannot take a scalar argument.") # L1 norm for vectors. elif value.is_vector(): return norm(value[1:] - value[0:max(rows, cols) - 1], 1) # L2 norm for matrices. else: row_diff = value[0:rows - 1, 1:cols] - value[0:rows - 1, 0:cols - 1] col_diff = value[1:rows, 0:cols - 1] - value[0:rows - 1, 0:cols - 1] return sum_entries(norm2_elemwise(row_diff, col_diff))
def norm(x, p=2, axis=None): """Wrapper on the different norm atoms. Parameters ---------- x : Expression or numeric constant The value to take the norm of. p : int or str, optional The type of norm. Returns ------- Expression An Expression representing the norm. """ x = Expression.cast_to_const(x) if p == 1: return pnorm(x, 1, axis) elif p == "inf": return pnorm(x, 'inf', axis) elif p == "nuc": return normNuc(x) elif p == "fro": return pnorm(x, 2, axis) elif p == 2: if x.is_matrix(): return sigma_max(x) else: return pnorm(x, 2, axis) else: return pnorm(x, p, axis)
def diff(x, k=1, axis=0): """ Vector of kth order differences. Takes in a vector of length n and returns a vector of length n-k of the kth order differences. diff(x) returns the vector of differences between adjacent elements in the vector, that is [x[2] - x[1], x[3] - x[2], ...] diff(x, 2) is the second-order differences vector, equivalently diff(diff(x)) diff(x, 0) returns the vector x unchanged """ x = Expression.cast_to_const(x) if axis == 1: x = x.T m, n = x.size if k < 0 or k >= m: raise ValueError('Must have k >= 0 and X must have < k elements along axis') d = x for i in range(k): d = d[1:, :] - d[:-1, :] if axis == 1: return d.T else: return d
def l1_aniso_2d(value1, value2): """\sum \sqrt{value1^2 + value2^2} Parameters ---------- value : Expression or numeric constant The value to take the total variation of. Returns ------- Expression An Expression representing the total variation. """ value1 = Expression.cast_to_const(value1) value2 = Expression.cast_to_const(value2) len = value1.size[0] return sum_entries(cvxnorm(value1 + value2, p='1'))
def lambda_sum_largest(X, k): """Sum of the largest k eigenvalues. """ X = Expression.cast_to_const(X) if X.size[0] != X.size[1]: raise ValueError("First argument must be a square matrix.") elif int(k) != k or k <= 0: raise ValueError("Second argument must be a positive integer.") """ S_k(X) denotes lambda_sum_largest(X, k) t >= k S_k(X - Z) + trace(Z), Z is PSD implies t >= ks + trace(Z) Z is PSD sI >= X - Z (PSD sense) which implies t >= ks + trace(Z) >= S_k(sI + Z) >= S_k(X) We use the fact that S_k(X) = sup_{sets of k orthonormal vectors u_i}\sum_{i}u_i^T X u_i and if Z >= X in PSD sense then \sum_{i}u_i^T Z u_i >= \sum_{i}u_i^T X u_i We have equality when s = lambda_k and Z diagonal with Z_{ii} = (lambda_i - lambda_k)_+ """ Z = Semidef(X.size[0]) return k*lambda_max(X - Z) + trace(Z)
def log_normcdf(x): """Elementwise log of the cumulative distribution function of a standard normal random variable. Implementation is a quadratic approximation with modest accuracy over [-4, 4]. """ A = scipy.sparse.diags( np.sqrt( [ 0.02301291, 0.08070214, 0.16411522, 0.09003495, 0.08200854, 0.01371543, 0.04641081, ] ) ) b = np.array([[3.0, 2.0, 1.0, 0.0, -1.0, -2.5, -3.5]]).reshape(-1, 1) x = Expression.cast_to_const(x) flat_x = reshape(x, (1, x.size)) y = A @ (b @ np.ones(flat_x.shape) - np.ones(b.shape) @ flat_x) out = -sum_(maximum(y, 0) ** 2, axis=0) return reshape(out, x.shape)
def promote(expr, shape): expr = Expression.cast_to_const(expr) if expr.shape != shape: if not expr.is_scalar(): raise ValueError('Only scalars may be promoted.') return Promote(expr, shape) else: return expr
def __init__(self, expr): self.args = [Expression.cast_to_const(expr)] # Validate that the objective resolves to a scalar. if not self.args[0].is_scalar(): raise ValueError("The '%s' objective must resolve to a scalar." % self.NAME) if not self.args[0].is_real(): raise ValueError("The '%s' objective must be real valued." % self.NAME)
def tvnorm_trace_2d(value1, value2, Dx, Dy): """Total variation of a vector, matrix, or list of matrices. Uses L1 norm of discrete gradients for vectors and L2 norm of discrete gradients for matrices. Parameters ---------- value : Expression or numeric constant The value to take the total variation of. Returns ------- Expression An Expression representing the total variation. """ value1 = Expression.cast_to_const(value1) value2 = Expression.cast_to_const(value2) len = value1.size[0] diffs = [Dx * (value1 + value2), Dy * (value1 + value2)] stack = vstack(*[reshape(diff, 1, len) for diff in diffs]) return sum_entries(cvxnorm(stack, p='fro', axis=0))
def promote(expr: Expression, shape: Tuple[int, ...]): """ Promote a scalar expression to a vector/matrix. Parameters ---------- expr : Expression The expression to promote. shape : tuple The shape to promote to. Raises ------ ValueError If ``expr`` is not a scalar. """ expr = Expression.cast_to_const(expr) if expr.shape != shape: if not expr.is_scalar(): raise ValueError('Only scalars may be promoted.') return Promote(expr, shape) else: return expr
def vec(X): """Flattens the matrix X into a vector in column-major order. Parameters ---------- X : Expression or numeric constant The matrix to flatten. Returns ------- Expression An Expression representing the flattened matrix. """ X = Expression.cast_to_const(X) return reshape(X, (X.size, ))
def vec(X): """Flattens the matrix X into a vector in column-major order. Parameters ---------- X : Expression or numeric constant The matrix to flatten. Returns ------- Expression An Expression representing the flattened matrix. """ X = Expression.cast_to_const(X) return reshape(X, X.size[0]*X.size[1], 1)
def expr_as_np_array(cvx_expr: Expression) -> np.ndarray: """ Convert cvxpy expression into a numpy array. :param cvx_expr: The cvxpy expression to be converted. :return: The numpy array of the cvxpy expression. """ if cvx_expr.is_scalar(): return np.array(cvx_expr) if len(cvx_expr.shape) == 1: return np.array(list(cvx_expr)) # Then cvx_expr is a 2-D array. rows = [] for i in range(cvx_expr.shape[0]): row = [cvx_expr[i, j] for j in range(cvx_expr.shape[1])] rows.append(row) arr = np.array(rows) return arr
def norm(x, p=2, axis=None): """Wrapper on the different norm atoms. Parameters ---------- x : Expression or numeric constant The value to take the norm of. If `x` is 2D and `axis` is None, this function constructs a matrix norm. p : int or str, optional The type of norm. Valid options include any positive integer, 'fro' (for frobenius), 'nuc' (sum of singular values), np.inf or 'inf' (infinity norm). axis : The axis along which to apply the norm, if any. Returns ------- Expression An Expression representing the norm. """ x = Expression.cast_to_const(x) # matrix norms take precedence num_nontrivial_idxs = sum([d > 1 for d in x.shape]) if axis is None and x.ndim == 2: if p == 1: # matrix 1-norm return cvxpy.atoms.max(norm1(x, axis=0)) # Frobenius norm elif p == 'fro' or (p == 2 and num_nontrivial_idxs == 1): return pnorm(vec(x), 2) elif p == 2: # matrix 2-norm is largest singular value return sigma_max(x) elif p == 'nuc': # the nuclear norm (sum of singular values) return normNuc(x) elif p in [np.inf, "inf", "Inf"]: # the matrix infinity-norm return cvxpy.atoms.max(norm1(x, axis=1)) else: raise RuntimeError('Unsupported matrix norm.') else: if p == 1 or x.is_scalar(): return norm1(x, axis=axis) elif p in [np.inf, "inf", "Inf"]: return norm_inf(x, axis) else: return pnorm(x, p, axis)
def tv(value, *args): """Total variation of a vector, matrix, or list of matrices. Uses L1 norm of discrete gradients for vectors and L2 norm of discrete gradients for matrices. Parameters ---------- value : Expression or numeric constant The value to take the total variation of. args : Matrix constants/expressions Additional matrices extending the third dimension of value. Returns ------- Expression An Expression representing the total variation. """ # Accept single list as argument. if isinstance(value, list) and len(args) == 0: args = value[1:] value = value[0] value = Expression.cast_to_const(value) rows, cols = value.size if value.is_scalar(): raise ValueError("tv cannot take a scalar argument.") # L1 norm for vectors. elif value.is_vector(): return norm(value[1:] - value[0:max(rows, cols)-1], 1) # L2 norm for matrices. else: args = map(Expression.cast_to_const, args) values = [value] + list(args) diffs = [] for mat in values: diffs += [ mat[0:rows-1, 1:cols] - mat[0:rows-1, 0:cols-1], mat[1:rows, 0:cols-1] - mat[0:rows-1, 0:cols-1], ] length = diffs[0].size[0]*diffs[1].size[1] stacked = vstack(*[reshape(diff, 1, length) for diff in diffs]) return sum_entries(norm(stacked, p='fro', axis=0))
def tv(value, *args): """Total variation of a vector, matrix, or list of matrices. Uses L1 norm of discrete gradients for vectors and L2 norm of discrete gradients for matrices. Parameters ---------- value : Expression or numeric constant The value to take the total variation of. args : Matrix constants/expressions Additional matrices extending the third dimension of value. Returns ------- Expression An Expression representing the total variation. """ # Accept single list as argument. if isinstance(value, list) and len(args) == 0: args = value[1:] value = value[0] value = Expression.cast_to_const(value) rows, cols = value.size if value.is_scalar(): raise ValueError("tv cannot take a scalar argument.") # L1 norm for vectors. elif value.is_vector(): return norm(value[1:] - value[0:max(rows, cols)-1], 1) # L2 norm for matrices. else: args = list(map(Expression.cast_to_const, args)) values = [value] + list(args) diffs = [] for mat in values: diffs += [ mat[0:rows-1, 1:cols] - mat[0:rows-1, 0:cols-1], mat[1:rows, 0:cols-1] - mat[0:rows-1, 0:cols-1], ] length = diffs[0].size[0]*diffs[1].size[1] stacked = vstack(*[reshape(diff, 1, length) for diff in diffs]) return sum_entries(norm(stacked, p='fro', axis=0))
def sos_to_sdp(syms, poly, z): deg = len(z) A, b = construct_constraints(syms, poly, z) obj = cvx.Minimize(0) q = cvx.Variable(deg * deg, 1) X = Expression.cast_to_const(q) #Find a solution to the equality constraints for setting up the starting strongly feasible point constraints = [-A * q == b] prob = cvx.Problem(obj, constraints) prob.solve() Q0 = np.reshape(np.array(q.value), (deg, deg)) largest_eval = np.max(np.linalg.eigvals(Q0)) if (largest_eval <= 1e-10): return (prob.status, -np.matrix(Q0)) s0 = largest_eval + 10 #initialize starting points Q = cvx.Variable(deg, deg) q = cvx.vec(Q) s = cvx.Variable() Q.value = Q0 s.value = s0 m = b.shape[0] eps = 0.0001 t = 0.1 mu = 1.5 iteration = 1 print("Running barrier method:") while (s.value > 0) and (m / t > eps): obj = cvx.Minimize(s - (1 / t) * atom.log_det(s * np.eye(deg) - Q)) constraints = [-A * q == b, Q == Q.T, q == cvx.vec(Q)] prob = cvx.Problem(obj, constraints) prob.solve() #print(Q.value) #print(obj.value) print("Iteration: {} Value of s: {}".format(iteration, s.value)) t *= mu iteration += 1 return (prob.status, -np.matrix(Q.value))
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 harmonic_mean(x): """The harmonic mean of ``x``. Parameters ---------- x : Expression or numeric The expression whose harmonic mean is to be computed. Must have positive entries. Returns ------- Expression .. math:: \\frac{n}{\\left(\\sum_{i=1}^{n} x_i^{-1} \\right)}, where :math:`n` is the length of :math:`x`. """ x = Expression.cast_to_const(x) # TODO(akshayka): Behavior of the below is incorrect when x has negative # entries. Either fail fast or provide a correct expression with # unknown curvature. return x.size*pnorm(x, -1)
def tv(value, *args): """Total variation of a vector, matrix, or list of matrices. Uses L1 norm of discrete gradients for vectors and L2 norm of discrete gradients for matrices. Parameters ---------- value : Expression or numeric constant The value to take the total variation of. args : Matrix constants/expressions Additional matrices extending the third dimension of value. Returns ------- Expression An Expression representing the total variation. """ value = Expression.cast_to_const(value) if value.ndim == 0: raise ValueError("tv cannot take a scalar argument.") # L1 norm for vectors. elif value.ndim == 1: return norm(value[1:] - value[0:value.shape[0]-1], 1) # L2 norm for matrices. else: rows, cols = value.shape args = map(Expression.cast_to_const, args) values = [value] + list(args) diffs = [] for mat in values: diffs += [ mat[0:rows-1, 1:cols] - mat[0:rows-1, 0:cols-1], mat[1:rows, 0:cols-1] - mat[0:rows-1, 0:cols-1], ] length = diffs[0].shape[0]*diffs[1].shape[1] stacked = vstack([reshape(diff, (1, length)) for diff in diffs]) return sum(norm(stacked, p=2, axis=0))
def mixed_norm(X, p=2, q=1): """Lp,q norm; :math:`(\\sum_k (\\sum_l \\lvert x_{k,l} \\rvert^p)^{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, p, axis=1) # outer norm return norm(vecnorms, q)
def norm(x, p=2, axis=None): """Wrapper on the different norm atoms. Parameters ---------- x : Expression or numeric constant The value to take the norm of. p : int or str, optional The type of norm. Returns ------- Expression An Expression representing the norm. """ x = Expression.cast_to_const(x) # matrix norms take precedence if axis is None and x.ndim == 2: if p == 1: # matrix 1-norm return cvxpy.atoms.max(norm1(x, axis=0)) elif p == 2: # matrix 2-norm is largest singular value return sigma_max(x) elif p == 'nuc': # the nuclear norm (sum of singular values) return normNuc(x) elif p == 'fro': # Frobenius norm return pnorm(vec(x), 2) elif p in [np.inf, "inf", "Inf"]: # the matrix infinity-norm return cvxpy.atoms.max(norm1(x, axis=1)) else: raise RuntimeError('Unsupported matrix norm.') else: if p == 1 or x.is_scalar(): return norm1(x, axis=axis) elif p in [np.inf, "inf", "Inf"]: return norm_inf(x, axis) else: return pnorm(x, p, axis)
def tv(value, *args): """Total variation of a vector, matrix, or list of matrices. Uses L1 norm of discrete gradients for vectors and L2 norm of discrete gradients for matrices. Parameters ---------- value : Expression or numeric constant The value to take the total variation of. args : Matrix constants/expressions Additional matrices extending the third dimension of value. Returns ------- Expression An Expression representing the total variation. """ value = Expression.cast_to_const(value) rows, cols = value.size if value.is_scalar(): raise ValueError("tv cannot take a scalar argument.") # L1 norm for vectors. elif value.is_vector(): return norm(value[1:] - value[0:max(rows, cols)-1], 1) # L2 norm for matrices. else: args = list(map(Expression.cast_to_const, args)) values = [value] + list(args) diffs = [] for mat in values: diffs += [ mat[0:rows-1, 1:cols] - mat[0:rows-1, 0:cols-1], mat[1:rows, 0:cols-1] - mat[0:rows-1, 0:cols-1], ] return sum_entries(norm2_elemwise(*diffs))
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 tv(value, *args): """Total variation of a vector, matrix, or list of matrices. Uses L1 norm of discrete gradients for vectors and L2 norm of discrete gradients for matrices. Parameters ---------- value : Expression or numeric constant The value to take the total variation of. args : Matrix constants/expressions Additional matrices extending the third dimension of value. Returns ------- Expression An Expression representing the total variation. """ value = Expression.cast_to_const(value) rows, cols = value.size if value.is_scalar(): raise ValueError("tv cannot take a scalar argument.") # L1 norm for vectors. elif value.is_vector(): return norm(value[1:] - value[0:max(rows, cols) - 1], 1) # L2 norm for matrices. else: args = map(Expression.cast_to_const, args) values = [value] + list(args) diffs = [] for mat in values: diffs += [ mat[0:rows - 1, 1:cols] - mat[0:rows - 1, 0:cols - 1], mat[1:rows, 0:cols - 1] - mat[0:rows - 1, 0:cols - 1], ] return sum_entries(norm2_elemwise(*diffs))
def min_entries(x, axis=None): """:math:`\min_{i,j}\{X_{i,j}\}`. """ x = Expression.cast_to_const(x) return -max_entries(-x, axis=axis)
def __init__(self, expr): self.args = [Expression.cast_to_const(expr)] # Validate that the objective resolves to a scalar. if self.args[0].size != (1, 1): raise Exception("The '%s' objective must resolve to a scalar." % self.NAME)
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 lambda_sum_smallest(X, k): """Sum of the largest k eigenvalues. """ X = Expression.cast_to_const(X) return -lambda_sum_largest(-X, k)
def sum_smallest(x, k): """Sum of the smallest k values. """ x = Expression.cast_to_const(x) return -sum_largest(-x, k)
def harmonic_mean(x): x = Expression.cast_to_const(x) return np.prod(x.size)*pnorm(x, -1)
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