def sum_largest_canon(expr, args): x = args[0] k = expr.k # min sum(t) + kq # s.t. x <= t + q # 0 <= t t = Variable(x.shape) q = Variable() obj = sum(t) + k * q constraints = [x <= t + q, t >= 0] return obj, constraints
def norm1_canon(expr, args): x = args[0] axis = expr.axis # we need an absolute value constraint for the symmetric convex branches # (p >= 1) constraints = [] # TODO(akshayka): Express this more naturally (recursively), in terms # of the other atoms abs_expr = abs(x) abs_x, abs_constraints = abs_canon(abs_expr, abs_expr.args) constraints += abs_constraints return sum(abs_x, axis=axis), constraints
def log_det_canon(expr, args): """Reduces the atom to an affine expression and list of constraints. Creates the equivalent problem:: maximize sum(log(D[i, i])) subject to: D diagonal diag(D) = diag(Z) Z is upper triangular. [D Z; Z.T A] is positive semidefinite The problem computes the LDL factorization: .. math:: A = (Z^TD^{-1})D(D^{-1}Z) This follows from the inequality: .. math:: \\det(A) >= \\det(D) + \\det([D, Z; Z^T, A])/\\det(D) >= \\det(D) because (Z^TD^{-1})D(D^{-1}Z) is a feasible D, Z that achieves det(A) = det(D) and the objective maximizes det(D). Parameters ---------- expr : log_det args : list The arguments for the expression Returns ------- tuple (Variable for objective, list of constraints) """ A = args[0] # n by n matrix. n, _ = A.shape z = Variable(shape=(n * (n + 1) // 2, )) Z = vec_to_upper_tri(z, strict=False) d = diag_mat(Z) # a vector D = diag_vec(d) # a matrix X = bmat([[D, Z], [Z.T, A]]) constraints = [PSD(X)] log_expr = log(d) obj, constr = log_canon(log_expr, log_expr.args) constraints += constr return sum(obj), constraints
def solve(self): self.levered_smile = list( filter(lambda g: not math.isnan(g.price), sorted(self.levered_smile, key=lambda g: g.strike))) self.unlevered_smile = list( filter(lambda g: not math.isnan(g.price), sorted(self.unlevered_smile, key=lambda g: g.strike))) lstrikes, ulstrikes = list(map(lambda g: g.strike, self.levered_smile)), \ list(map(lambda g: g.strike, self.unlevered_smile)) lprices, ulprices = list(map(lambda g: g.price, self.levered_smile)),\ list(map(lambda g: g.price, self.unlevered_smile)) lpcts, ulpcts = list(map(lambda strike: (strike - self.levered_spot)/self.levered_spot, lstrikes)), \ list(map(lambda strike: (strike - self.unlevered_spot) / self.unlevered_spot, ulstrikes)) ltheta, ultheta = list(map(lambda g: g.theta, self.levered_smile)),\ list(map(lambda g: g.theta, self.unlevered_smile)) lcontract, ulcontract = cp.Variable( len(lstrikes), boolean=True), cp.Variable(len(ulstrikes), boolean=True) spot_ratio = self.levered_spot / self.unlevered_spot self.standardized_lratio = round(self.levered_ratio * spot_ratio) max_theta = False objective = cx.sum(-self.unlevered_ratio * lcontract * ltheta + self.standardized_lratio * ulcontract * ultheta) \ if max_theta else cx.sum(self.unlevered_ratio * lcontract * lprices - self.standardized_lratio * ulcontract * ulprices) prob = cp.Problem(100 * cp.Maximize(objective), [ cx.sum(lcontract) == 1, cx.sum(ulcontract) == 1, cx.sum(self.levered_ratio * ulcontract * ulpcts) <= cx.sum( self.unlevered_ratio * lcontract * lpcts), cx.sum(self.safety_margin * self.standardized_lratio * ulcontract * ulpcts) <= cx.sum(self.unlevered_ratio * lcontract * lpcts) ]) ans = prob.solve(solver=cp.GLPK_MI) return ans, self.levered_smile[ArbSmile.idx( lcontract)], self.unlevered_smile[ArbSmile.idx(ulcontract)]
def pnorm_canon(expr, args): x = args[0] p = expr.p axis = expr.axis shape = expr.shape t = Variable(shape) if p == 2: if axis is None: assert shape == tuple() return t, [SOC(t, vec(x))] else: return t, [SOC(vec(t), x, axis)] # we need an absolute value constraint for the symmetric convex branches # (p > 1) constraints = [] if p > 1: # TODO(akshayka): Express this more naturally (recursively), in terms # of the other atoms abs_expr = abs(x) abs_x, abs_constraints = abs_canon(abs_expr, abs_expr.args) x = abs_x constraints += abs_constraints # now, we take care of the remaining convex and concave branches # to create the rational powers, we need a new variable, r, and # the constraint sum(r) == t r = Variable(x.shape) constraints += [sum(r) == t] # todo: no need to run gm_constr to form the tree each time. # we only need to form the tree once promoted_t = Constant(np.ones(x.shape)) * t p = Fraction(p) if p < 0: constraints += gm_constrs(promoted_t, [x, r], (-p / (1 - p), 1 / (1 - p))) if 0 < p < 1: constraints += gm_constrs(r, [x, promoted_t], (p, 1 - p)) if p > 1: constraints += gm_constrs(x, [r, promoted_t], (1 / p, 1 - 1 / p)) return t, constraints
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 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 log_det_canon(expr, args): """Reduces the atom to an affine expression and list of constraints. Creates the equivalent problem:: maximize sum(log(D[i, i])) subject to: D diagonal diag(D) = diag(Z) Z is upper triangular. [D Z; Z.T A] is positive semidefinite The problem computes the LDL factorization: .. math:: A = (Z^TD^{-1})D(D^{-1}Z) This follows from the inequality: .. math:: \\det(A) >= \\det(D) + \\det([D, Z; Z^T, A])/\\det(D) >= \\det(D) because (Z^TD^{-1})D(D^{-1}Z) is a feasible D, Z that achieves det(A) = det(D) and the objective maximizes det(D). Parameters ---------- expr : log_det args : list The arguments for the expression Returns ------- tuple (Variable for objective, list of constraints) """ A = args[0] # n by n matrix. n, _ = A.shape # Require that X and A are PSD. X = Variable((2 * n, 2 * n), PSD=True) constraints = [PSD(A)] # Fix Z as upper triangular # TODO represent Z as upper tri vector. Z = Variable((n, n)) Z_lower_tri = upper_tri(transpose(Z)) constraints.append(Z_lower_tri == 0) # Fix diag(D) = diag(Z): D[i, i] = Z[i, i] D = Variable(n) constraints.append(D == diag_mat(Z)) # Fix X using the fact that A must be affine by the DCP rules. # X[0:n, 0:n] == D constraints.append(X[0:n, 0:n] == diag_vec(D)) # X[0:n, n:2*n] == Z, constraints.append(X[0:n, n:2 * n] == Z) # X[n:2*n, n:2*n] == A constraints.append(X[n:2 * n, n:2 * n] == A) # Add the objective sum(log(D[i, i]) log_expr = log(D) obj, constr = log_canon(log_expr, log_expr.args) constraints += constr return sum(obj), constraints
def norm1_canon(expr, args): assert len(args) == 1 tmp = sum(args[0], expr.axis, expr.keepdims) return sum_canon(tmp, tmp.args)