/
nlp2.jl
232 lines (191 loc) · 9.2 KB
/
nlp2.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#*******************************************************/
#* Copyright(c) 2018 by Artelys */
#* This source code is subject to the terms of the */
#* MIT Expat License (see LICENSE.md) */
#*******************************************************/
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# This example demonstrates how to use Knitro to solve the following
# simple nonlinear optimization problem. This model is test problem
# HS40 from the Hock & Schittkowski collection.
#
# max x0*x1*x2*x3 (obj)
# s.t. x0^3 + x1^2 = 1 (c0)
# x0^2*x3 - x2 = 0 (c1)
# x3^2 - x1 = 0 (c2)
#
# This example also shows show to use the "newpt" callback, which
# can be used to perform some user-defined task every time Knitro
# iterates to a new solution estimate(e.g. it can be used to define
# a customized stopping condition).
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
using KNITRO, Test
#*------------------------------------------------------------------*
#* FUNCTION callbackEvalFC *
#*------------------------------------------------------------------*
# The signature of this function matches KNITRO.KN_eval_callback in knitro.h.
# Only "obj" and "c" are set in the KNITRO.KN_eval_result structure.
function callbackEvalFC(kc, cb, evalRequest, evalResult, userParams)
x = evalRequest.x
# Evaluate nonlinear term in objective
evalResult.obj[1] = x[1] * x[2] * x[3] * x[4]
# Evaluate nonlinear terms in constraints
evalResult.c[1] = x[1] * x[1] * x[1]
evalResult.c[2] = x[1] * x[1] * x[4]
return 0
end
#*------------------------------------------------------------------*
#* FUNCTION callbackEvalGA *
#*------------------------------------------------------------------*
# The signature of this function matches KNITRO.KN_eval_callback in knitro.h.
# Only "objGrad" and "jac" are set in the KNITRO.KN_eval_result structure.
function callbackEvalGA(kc, cb, evalRequest, evalResult, userParams)
x = evalRequest.x
# Evaluate nonlinear term in objective gradient
evalResult.objGrad[1] = x[2] * x[3] * x[4]
evalResult.objGrad[2] = x[1] * x[3] * x[4]
evalResult.objGrad[3] = x[1] * x[2] * x[4]
evalResult.objGrad[4] = x[1] * x[2] * x[3]
# Evaluate nonlinear terms in constraint gradients(Jacobian)
evalResult.jac[1] = 3.0 * x[1] * x[1] # derivative of x0^3 term wrt x0
evalResult.jac[2] = 2.0 * x[1] * x[4] # derivative of x0^2 * x3 term wrt x0
evalResult.jac[3] = x[1] * x[1] # derivative of x0^2 * x3 terms wrt x3
return 0
end
#*------------------------------------------------------------------*
#* FUNCTION callbackEvalH *
#*------------------------------------------------------------------*
# The signature of this function matches KNITRO.KN_eval_callback in knitro.h.
# Only "hess" or "hessVec" are set in the KNITRO.KN_eval_result structure.
function callbackEvalH(kc, cb, evalRequest, evalResult, userParams)
x = evalRequest.x
lambda_ = evalRequest.lambda
# Scale objective component of hessian by sigma
sigma = evalRequest.sigma
# Evaluate nonlinear term in the Hessian of the Lagrangian.
# Note: If sigma=0, some computations can be avoided.
if sigma > 0.0 # Evaluate the full Hessian of the Lagrangian
evalResult.hess[1] = lambda_[1] * 6.0 * x[1] + lambda_[2] * 2.0 * x[4]
evalResult.hess[2] = sigma * x[3] * x[4]
evalResult.hess[3] = sigma * x[2] * x[4]
evalResult.hess[4] = sigma * x[2] * x[3] + lambda_[2] * 2.0 * x[1]
evalResult.hess[5] = sigma * x[1] * x[4]
evalResult.hess[6] = sigma * x[1] * x[3]
evalResult.hess[7] = sigma * x[1] * x[2]
else # sigma=0, do not include objective component
evalResult.hess[1] = lambda_[1] * 6.0 * x[1] + lambda_[2] * 2.0 * x[4]
evalResult.hess[2] = 0.0
evalResult.hess[3] = 0.0
evalResult.hess[4] = lambda_[2] * 2.0 * x[1]
evalResult.hess[5] = 0.0
evalResult.hess[6] = 0.0
evalResult.hess[7] = 0.0
end
return 0
end
#*------------------------------------------------------------------*
#* FUNCTION callbackNewPoint *
#*------------------------------------------------------------------*
# The signature of this function matches KNITRO.KN_user_callback in
# knitro.h. Nothing should be modified. This example printlns out
# that Knitro has iterated to a new point(x, lambda) that it
# considers an improvement over the previous iterate, and printlns
# out the current feasibility error and number of evaluations.
# To exercise it, edit "knitro.opt" and set the the "newpoint"
# option to "user". The demonstration looks best if the "outlev"
# option is set to 5 or 6.
function callbackNewPoint(kc, x, lambda_, userParams)
# Get the number of variables in the model
n = KNITRO.KN_get_number_vars(kc)
println(">> New point computed by Knitro:(", x, ")")
# Query information about the current problem.
dFeasError = KNITRO.KN_get_abs_feas_error(kc)
println("Number FC evals= ", KNITRO.KN_get_number_FC_evals(kc))
println("Current feasError= " , dFeasError)
# Demonstrate user-defined termination
#(Uncomment to activate)
if KNITRO.KN_get_obj_value(kc) > 0.2 && dFeasError <= 1.0e-4
return KNITRO.KN_RC_USER_TERMINATION
end
return 0
end
#*------------------------------------------------------------------*
#* main *
#*------------------------------------------------------------------*
# Create a new Knitro solver instance.
kc = KNITRO.KN_new()
# Initialize Knitro with the problem definition.
# Add the variables and specify initial values for them.
# Note: any unset lower bounds are assumed to be
# unbounded below and any unset upper bounds are
# assumed to be unbounded above.
xIndices = KNITRO.KN_add_vars(kc, 4)
for x in xIndices
KNITRO.KN_set_var_primal_init_values(kc, x, 0.8)
end
# Add the constraints and set the rhs and coefficients
KNITRO.KN_add_cons(kc, 3)
KNITRO.KN_set_con_eqbnds(kc, [1.0, 0.0, 0.0])
# Coefficients for 2 linear terms
lconIndexCons = Int32[1, 2]
lconIndexVars = Int32[2, 1]
lconCoefs = [-1.0, -1.0]
KNITRO.KN_add_con_linear_struct(kc, lconIndexCons, lconIndexVars, lconCoefs)
# Coefficients for 2 quadratic terms
# 1st term: x1^2 term in c0
# 2nd term: x3^2 term in c2
qconIndexCons = Int32[0, 2]
qconIndexVars1 = Int32[1, 3]
qconIndexVars2 = Int32[1, 3]
qconCoefs = [1.0, 1.0]
KNITRO.KN_add_con_quadratic_struct(kc, qconIndexCons, qconIndexVars1, qconIndexVars2, qconCoefs)
# Add callback to evaluate nonlinear(non-quadratic) terms in the model:
# x0*x1*x2*x3 in the objective
# x0^3 in first constraint c0
# x0^2*x3 in second constraint c1
cb = KNITRO.KN_add_eval_callback(kc, true, Int32[0, 1], callbackEvalFC)
# Set obj. gradient and nonlinear jac provided through callbacks.
# Mark objective gradient as dense, and provide non-zero sparsity
# structure for constraint Jacobian terms.
cbjacIndexCons = Int32[0, 1, 1]
cbjacIndexVars = Int32[0, 0, 3]
KNITRO.KN_set_cb_grad(kc, cb, callbackEvalGA, jacIndexCons=cbjacIndexCons, jacIndexVars=cbjacIndexVars)
# Set nonlinear Hessian provided through callbacks. Since the
# Hessian is symmetric, only the upper triangle is provided.
# The upper triangular Hessian for nonlinear callback structure is:
# # lambda0*6*x0 + lambda1*2*x3 x2*x3 x1*x3 x1*x2 + lambda1*2*x0
# 0 0 x0*x3 x0*x2
# 0 x0*x1
# 0
#(7 nonzero elements)
cbhessIndexVars1 = Int32[0, 0, 0, 0, 1, 1, 2]
cbhessIndexVars2 = Int32[0, 1, 2, 3, 2, 3, 3]
KNITRO.KN_set_cb_hess(kc, cb, length(cbhessIndexVars1), callbackEvalH,
hessIndexVars1=cbhessIndexVars1, hessIndexVars2=cbhessIndexVars2)
# Set minimize or maximize(if not set, assumed minimize)
KNITRO.KN_set_obj_goal(kc, KNITRO.KN_OBJGOAL_MAXIMIZE)
# Demonstrate setting a "newpt" callback. the callback function
# "callbackNewPoint" passed here is invoked after Knitro computes
# a new estimate of the solution.
KNITRO.KN_set_newpt_callback(kc, callbackNewPoint)
# Set option to println output after every iteration.
KNITRO.KN_set_param(kc, KNITRO.KN_PARAM_OUTLEV, KNITRO.KN_OUTLEV_ITER)
# Solve the problem.
#
# Return status codes are defined in "knitro.h" and described
# in the Knitro manual.
nStatus = KNITRO.KN_solve(kc)
println()
println("Knitro converged with final status = ", nStatus)
# An example of obtaining solution information.
nStatus, objSol, x, lambda_ = KNITRO.KN_get_solution(kc)
println(" optimal objective value = ", objSol)
println(" optimal primal values x = ", x)
println(" feasibility violation = ", KNITRO.KN_get_abs_feas_error(kc))
println(" KKT optimality violation = ", KNITRO.KN_get_abs_opt_error(kc))
# Delete the Knitro solver instance.
KNITRO.KN_free(kc)
@testset "Example HS40 nlp2" begin
@test nStatus == KNITRO.KN_RC_USER_TERMINATION
@test objSol ≈ 0.25 atol=1e-4
@test x ≈ [0.793701, 0.707107, 0.529732, 0.840896] atol=1e-4
end