1""" 2Copyright 2013 Steven Diamond 3 4 5Licensed under the Apache License, Version 2.0 (the "License"); 6you may not use this file except in compliance with the License. 7You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11Unless required by applicable law or agreed to in writing, software 12distributed under the License is distributed on an "AS IS" BASIS, 13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14See the License for the specific language governing permissions and 15limitations under the License. 16""" 17 18from typing import List, Tuple 19 20import numpy as np 21 22from cvxpy.constraints.constraint import Constraint 23from cvxpy.expressions import cvxtypes 24from cvxpy.utilities import scopes 25 26 27class ExpCone(Constraint): 28 """A reformulated exponential cone constraint. 29 30 Operates elementwise on :math:`x, y, z`. 31 32 Original cone: 33 34 .. math:: 35 36 K = \\{(x,y,z) \\mid y > 0, ye^{x/y} <= z\\} 37 \\cup \\{(x,y,z) \\mid x \\leq 0, y = 0, z \\geq 0\\} 38 39 Reformulated cone: 40 41 .. math:: 42 43 K = \\{(x,y,z) \\mid y, z > 0, y\\log(y) + x \\leq y\\log(z)\\} 44 \\cup \\{(x,y,z) \\mid x \\leq 0, y = 0, z \\geq 0\\} 45 46 Parameters 47 ---------- 48 x : Variable 49 x in the exponential cone. 50 y : Variable 51 y in the exponential cone. 52 z : Variable 53 z in the exponential cone. 54 """ 55 56 def __init__(self, x, y, z, constr_id=None) -> None: 57 Expression = cvxtypes.expression() 58 self.x = Expression.cast_to_const(x) 59 self.y = Expression.cast_to_const(y) 60 self.z = Expression.cast_to_const(z) 61 super(ExpCone, self).__init__([self.x, self.y, self.z], 62 constr_id) 63 64 def __str__(self) -> str: 65 return "ExpCone(%s, %s, %s)" % (self.x, self.y, self.z) 66 67 def __repr__(self) -> str: 68 return "ExpCone(%s, %s, %s)" % (self.x, self.y, self.z) 69 70 @property 71 def residual(self): 72 # TODO(akshayka): The projection should be implemented directly. 73 from cvxpy import Minimize, Problem, Variable, hstack, norm2 74 if self.x.value is None or self.y.value is None or self.z.value is None: 75 return None 76 x = Variable(self.x.shape) 77 y = Variable(self.y.shape) 78 z = Variable(self.z.shape) 79 constr = [ExpCone(x, y, z)] 80 obj = Minimize(norm2(hstack([x, y, z]) - 81 hstack([self.x.value, self.y.value, self.z.value]))) 82 problem = Problem(obj, constr) 83 return problem.solve() 84 85 @property 86 def size(self) -> int: 87 """The number of entries in the combined cones. 88 """ 89 return 3 * self.num_cones() 90 91 def num_cones(self): 92 """The number of elementwise cones. 93 """ 94 return self.x.size 95 96 def cone_sizes(self) -> List[int]: 97 """The dimensions of the exponential cones. 98 99 Returns 100 ------- 101 list 102 A list of the sizes of the elementwise cones. 103 """ 104 return [3]*self.num_cones() 105 106 def is_dcp(self, dpp: bool = False) -> bool: 107 """An exponential constraint is DCP if each argument is affine. 108 """ 109 if dpp: 110 with scopes.dpp_scope(): 111 return all(arg.is_affine() for arg in self.args) 112 return all(arg.is_affine() for arg in self.args) 113 114 def is_dgp(self, dpp: bool = False) -> bool: 115 return False 116 117 def is_dqcp(self) -> bool: 118 return self.is_dcp() 119 120 @property 121 def shape(self) -> Tuple[int, ...]: 122 s = (3,) + self.x.shape 123 return s 124 125 def save_dual_value(self, value) -> None: 126 # TODO(akshaya,SteveDiamond): verify that reshaping below works correctly 127 value = np.reshape(value, newshape=(-1, 3)) 128 dv0 = np.reshape(value[:, 0], newshape=self.x.shape) 129 dv1 = np.reshape(value[:, 1], newshape=self.y.shape) 130 dv2 = np.reshape(value[:, 2], newshape=self.z.shape) 131 self.dual_variables[0].save_value(dv0) 132 self.dual_variables[1].save_value(dv1) 133 self.dual_variables[2].save_value(dv2) 134