|
32 | 32 | from cvxpy.atoms.affine.sum import sum as cvxpy_sum |
33 | 33 | from cvxpy.constraints.constraint import Constraint |
34 | 34 | from cvxpy.error import DCPError |
35 | | -from cvxpy.expressions.constants.constant import Constant |
36 | 35 | from cvxpy.expressions.constants.parameter import ( |
37 | 36 | is_param_affine, |
38 | 37 | is_param_free, |
@@ -235,33 +234,60 @@ def _grad(self, values): |
235 | 234 |
|
236 | 235 | return [DX, DY] |
237 | 236 |
|
238 | | - def _hess(self, values): |
239 | | - """Compute the Hessian of elementwise multiplication w.r.t. each argument. |
| 237 | + def _verify_hess_vec_args(self): |
| 238 | + x = self.args[0] |
| 239 | + y = self.args[1] |
| 240 | + if x.size != y.size: |
| 241 | + return False |
240 | 242 |
|
241 | | - For z = x * y (elementwise), returns: |
242 | | - - d2z/dx2 = diag(y) |
243 | | - - d2z/dy2 = diag(x) |
| 243 | + if x.is_constant() and y.is_constant(): |
| 244 | + return False |
244 | 245 |
|
245 | | - Args: |
246 | | - values: A list of numeric values for the arguments [x, y]. |
| 246 | + # one of the following must be true: |
| 247 | + # 1. both arguments are variables |
| 248 | + # 2. one argument is a constant |
| 249 | + # 3. one argument is a Promote of a variable and the other is a variable |
| 250 | + both_are_variables = isinstance(x, Variable) and isinstance(y, Variable) |
| 251 | + one_is_constant = x.is_constant() or y.is_constant() |
| 252 | + x_is_promote = type(x) == Promote and isinstance(y, Variable) |
| 253 | + y_is_promote = type(y) == Promote and isinstance(x, Variable) |
247 | 254 |
|
248 | | - Returns: |
249 | | - A list of SciPy CSC sparse matrices [D2X, D2Y]. |
250 | | - """ |
251 | | - if isinstance(self.args[0], Variable) and isinstance(self.args[1], Variable): |
252 | | - return {(self.args[0], self.args[1]): np.eye(self.size), |
253 | | - (self.args[1], self.args[0]): np.eye(self.size)} |
254 | | - if isinstance(self.args[0], Constant) and isinstance(self.args[1], Variable): |
255 | | - return self.args[1].hess |
256 | | - x = values[0] |
257 | | - y = values[1] |
258 | | - # what is the hessian of elementwise multiplication? |
259 | | - # Flatten in case inputs are not 1D |
260 | | - x = np.asarray(x).flatten(order='F') |
261 | | - y = np.asarray(y).flatten(order='F') |
262 | | - D2X = sp.diags(y, format='csc') |
263 | | - D2Y = sp.diags(x, format='csc') |
264 | | - return [D2X, D2Y] |
| 255 | + if not (both_are_variables or one_is_constant or x_is_promote or y_is_promote): |
| 256 | + return False |
| 257 | + |
| 258 | + if both_are_variables and x.id == y.id: |
| 259 | + return False |
| 260 | + |
| 261 | + return True |
| 262 | + |
| 263 | + def _hess_vec(self, vec): |
| 264 | + x = self.args[0] |
| 265 | + y = self.args[1] |
| 266 | + |
| 267 | + # constant * atom |
| 268 | + if x.is_constant(): |
| 269 | + y_hess_vec = y.hess_vec(x.value * vec) |
| 270 | + return y_hess_vec |
| 271 | + |
| 272 | + # atom * constant |
| 273 | + if y.is_constant(): |
| 274 | + x_hess_vec = x.hess_vec(y.value * vec) |
| 275 | + return x_hess_vec |
| 276 | + |
| 277 | + # x * y with x a scalar variable, y a vector variable |
| 278 | + if not isinstance(x, Variable) and x.is_affine(): |
| 279 | + assert(type(x) == Promote) |
| 280 | + x_var = x.args[0] # here x is a Promote because of how we canonicalize |
| 281 | + return {(x_var, y): vec, (y, x_var): vec} |
| 282 | + |
| 283 | + # x * y with x a vector variable, y a scalar |
| 284 | + if not isinstance(y, Variable) and y.is_affine(): |
| 285 | + assert(type(y) == Promote) |
| 286 | + y_var = y.args[0] # here y is a Promote because of how we canonicalize |
| 287 | + return {(x, y_var): vec, (y_var, x): vec} |
| 288 | + |
| 289 | + # if we arrive here both arguments are variables of the same size |
| 290 | + return {(x, y): np.diag(vec), (y, x): np.diag(vec)} |
265 | 291 |
|
266 | 292 | def graph_implementation( |
267 | 293 | self, arg_objs, shape: Tuple[int, ...], data=None |
|
0 commit comments