21
21
# # User-defined Hessians
22
22
23
23
# In this tutorial, we explain how to write a user-defined function (see [User-defined Functions](@ref))
24
- # with an explicit Hessian matrix.
24
+ # with a Hessian matrix explicitly provided by the user .
25
25
26
26
# This tutorial uses the following packages:
27
27
@@ -34,7 +34,7 @@ import Ipopt
34
34
35
35
# ```math
36
36
# \begin{array}{r l}
37
- # \min\limits_{x} & x_1^2 + x_2^2 + z \\
37
+ # \min\limits_{x,z } & x_1^2 + x_2^2 + z \\
38
38
# s.t. & \begin{array}{r l}
39
39
# z \ge \max\limits_{y} & x_1^2 y_1 + x_2^2 y_2 - x_1 y_1^4 - 2 x_2 y_2^4 \\
40
40
# s.t. & (y_1 - 10)^2 + (y_2 - 10)^2 \le 25
@@ -45,16 +45,16 @@ import Ipopt
45
45
46
46
# This bilevel optimization problem is composed of two nested optimization
47
47
# problems. An _upper_ level, involving variables ``x``, and a _lower_ level,
48
- # involving variables ``y``. From the perspective of the lower-level, the
49
- # values of ``x`` are fixed parameters, and so the model optimizes ``y`` given
50
- # those fixed parameters. Simultaneously, the upper level is optimizing ``x``
51
- # given the response of ``y``.
48
+ # involving variables ``y``. From the perspective of the lower-level problem,
49
+ # the values of ``x`` are fixed parameters, and so the model optimizes ``y``
50
+ # given those fixed parameters. Simultaneously, the upper- level problem
51
+ # optimizes ``x`` and ``z`` given the response of ``y``.
52
52
53
53
# ## Decomposition
54
54
55
55
# There are a few ways to solve this problem, but we are going to use a
56
56
# nonlinear decomposition method. The first step is to write a function to
57
- # compute:
57
+ # compute the lower-level problem :
58
58
59
59
# ```math
60
60
# \begin{array}{r l}
@@ -78,7 +78,7 @@ function solve_lower_level(x...)
78
78
return objective_value (model), value .(y)
79
79
end
80
80
81
- # This function takes a guess of ``x`` and returns the optimal lower-level
81
+ # The next function takes a value of ``x`` and returns the optimal lower-level
82
82
# objective-value and the optimal response ``y``. The reason why we need both
83
83
# the objective and the optimal ``y`` will be made clear shortly, but for now
84
84
# let us define:
101
101
# ``V``! However, because ``V`` solves an optimization problem internally, we
102
102
# can't use automatic differentiation to compute the first and second
103
103
# derivatives. Instead, we can use JuMP's ability to pass callback functions
104
- # for the gradient and hessian instead.
104
+ # for the gradient and Hessian instead.
105
105
106
106
# First up, we need to define the gradient of ``V`` with respect to ``x``. In
107
107
# general, this may be difficult to compute, but because ``x`` appears only in
@@ -115,7 +115,7 @@ function ∇V(g::AbstractVector, x...)
115
115
return
116
116
end
117
117
118
- # Second, we need to define the hessian of ``V`` with respect to ``x``. This is
118
+ # Second, we need to define the Hessian of ``V`` with respect to ``x``. This is
119
119
# a symmetric matrix, but in our example only the diagonal elements are
120
120
# non-zero:
121
121
152
152
# ## Memoization
153
153
154
154
# Our solution approach works, but it has a performance problem: every time
155
- # we need to compute the value, gradient, or hessian of ``V``, we have to
155
+ # we need to compute the value, gradient, or Hessian of ``V``, we have to
156
156
# re-solve the lower-level optimization problem! This is wasteful, because we
157
- # will often call the gradient and hessian at the same point, and so solving the
157
+ # will often call the gradient and Hessian at the same point, and so solving the
158
158
# problem twice with the same input repeats work unnecessarily.
159
159
160
160
# We can work around this by using memoization:
161
161
162
162
function memoized_solve_lower_level ()
163
163
last_x, f, y = nothing , NaN , [NaN , NaN ]
164
164
function _update_if_needed (x... )
165
- if last_x != x
165
+ if last_x != = x
166
166
f, y = solve_lower_level (x... )
167
167
last_x = x
168
168
end
@@ -191,8 +191,8 @@ f, ∇f, ∇²f = memoized_solve_lower_level()
191
191
192
192
# The function above is a little confusing, but it returns three new functions
193
193
# `f`, `∇f`, and `∇²f`, each of which call `_update_if_needed(x...)`. This
194
- # function only updates the cached values of `f` and `y` if the input `x` is
195
- # different to what is last saw .
194
+ # function only updates the cached values of the objective `f` and lower-level
195
+ # primal variables `y` if the input `x` is different to its previous value .
196
196
197
197
model = Model (Ipopt. Optimizer)
198
198
@variable (model, x[1 : 2 ] >= 0 )
0 commit comments