Skip to content

Commit 115bf77

Browse files
Experiment: Hessian of the lagrangian (#31)
* attempting outline for hessian * first draft at a hessian --------- Co-authored-by: William Zijie Zhang <[email protected]> Co-authored-by: Daniel Cederberg <[email protected]>
1 parent 98c6cd6 commit 115bf77

File tree

2 files changed

+46
-8
lines changed

2 files changed

+46
-8
lines changed

cvxpy/atoms/atom.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,16 @@ def grad(self):
439439

440440
return result
441441

442+
def hess(self, duals):
443+
"""
444+
Compute the hessian-vector product of a scalar function with dual
445+
values.
446+
Here the dual values should correspond to the dual values of the
447+
constraint function or None if we are taking the hessian of the objective.
448+
We must first slice the duals array to get the relevant values.
449+
"""
450+
pass
451+
442452
@abc.abstractmethod
443453
def _grad(self, values):
444454
"""Gives the (sub/super)gradient of the atom w.r.t. each argument.

cvxpy/reductions/solvers/nlp_solvers/ipopt_nlpif.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ def __init__(self, problem, inital_point):
213213

214214
def objective(self, x):
215215
"""Returns the scalar value of the objective given x."""
216-
# Set the variable value
217216
offset = 0
218217
for var in self.main_var:
219218
size = var.size
@@ -227,7 +226,6 @@ def gradient(self, x):
227226
#import pdb
228227
#pdb.set_trace()
229228
"""Returns the gradient of the objective with respect to x."""
230-
# compute the gradient using _grad
231229
offset = 0
232230
for var in self.main_var:
233231
size = var.size
@@ -248,15 +246,13 @@ def gradient(self, x):
248246

249247
def constraints(self, x):
250248
"""Returns the constraint values."""
251-
# Set the variable value
252249
offset = 0
253250
#import pdb
254251
#pdb.set_trace()
255252
for var in self.main_var:
256253
size = var.size
257254
var.value = x[offset:offset+size].reshape(var.shape, order='F')
258255
offset += size
259-
260256
# Evaluate all constraints
261257
constraint_values = []
262258
for constraint in self.problem.constraints:
@@ -265,15 +261,12 @@ def constraints(self, x):
265261

266262
def jacobian(self, x):
267263
"""Returns only the non-zero values of the Jacobian."""
268-
#import pdb
269-
#pdb.set_trace()
270264
# Set variable values
271265
offset = 0
272266
for var in self.main_var:
273267
size = var.size
274268
var.value = x[offset:offset+size].reshape(var.shape, order='F')
275269
offset += size
276-
277270
values = []
278271
for constraint in self.problem.constraints:
279272
# get the jacobian of the constraint
@@ -329,9 +322,44 @@ def jacobianstructure(self):
329322
col_offset += var.size
330323
row_offset += constraint.size
331324
self.jacobian_idxs[constraint] = constraint_jac
332-
333325
return (np.array(rows), np.array(cols))
334326

327+
def hessian(self, x, duals, obj_factor):
328+
offset = 0
329+
for var in self.main_var:
330+
size = var.size
331+
var.value = x[offset:offset+size].reshape(var.shape, order='F')
332+
offset += size
333+
hess = np.zeros((x.size, x.size), dtype=np.float64)
334+
# hess_dict = self.problem.objective.expr.hess(obj_factor)
335+
# if we specify the problem in graph form (i.e. t=obj),
336+
# the objective hessian will always be zero.
337+
# To compute the hessian of the constraints, we need to gather the
338+
# second derivatives from each constraint.
339+
# This is done by looping through each constraint and each
340+
# pair of variables and summing up the hessian contributions.
341+
constr_offset = 0
342+
for constraint in self.problem.constraints:
343+
constr_hess = constraint.expr.hess()
344+
# we have to make sure that the dual variables correspond
345+
# to the constraints in the same order
346+
constr_dual = duals[constr_offset:constr_offset + constraint.size]
347+
row_offset = 0
348+
for var1 in self.main_var:
349+
col_offset = 0
350+
for var2 in self.main_var:
351+
hess[var1.index * row_offset, var2.index * col_offset] += (
352+
constr_dual * constr_hess.get((var1, var2), 0).toarray()
353+
)
354+
col_offset += var2.size
355+
row_offset += var1.size
356+
constr_offset += constraint.size
357+
return hess
358+
359+
def hessianstructure(self):
360+
pass
361+
362+
335363
class Bounds():
336364
def __init__(self, problem):
337365
self.problem = problem

0 commit comments

Comments
 (0)