1
1
# ## Constraints
2
- #
2
+ #
3
3
# Constraints are specified by the user as
4
4
# lx_i ≤ x[i] ≤ ux_i # variable (box) constraints
5
5
# lc_i ≤ c(x)[i] ≤ uc_i # linear/nonlinear constraints
6
6
# and become equality constraints with l_i = u_i. ±∞ are allowed for l
7
7
# and u, in which case the relevant side(s) are unbounded.
8
- #
8
+ #
9
9
# The user supplies functions to calculate c(x) and its derivatives.
10
- #
10
+ #
11
11
# Of course we could unify the box-constraints into the
12
12
# linear/nonlinear constraints, but that would force the user to
13
13
# provide the variable-derivatives manually, which would be silly.
14
- #
14
+ #
15
15
# This parametrization of the constraints gets "parsed" into a form
16
16
# that speeds and simplifies the IPNewton algorithm, at the cost of many
17
17
# additional variables. See `parse_constraints` for details.
@@ -35,7 +35,7 @@ function ConstraintBounds(lx, ux, lc, uc)
35
35
_cb (symmetrize (lx, ux)... , symmetrize (lc, uc)... )
36
36
end
37
37
function _cb (lx:: AbstractArray{Tx} , ux:: AbstractArray{Tx} , lc:: AbstractVector{Tc} , uc:: AbstractVector{Tc} ) where {Tx,Tc}
38
- T = promote_type (Tx,Tc)
38
+ T = promote_type (Tx, Tc)
39
39
ConstraintBounds {T} (length (lc), parse_constraints (T, lx, ux)... , parse_constraints (T, lc, uc)... )
40
40
end
41
41
@@ -114,8 +114,8 @@ function OnceDifferentiableConstraints(lx::AbstractArray, ux::AbstractArray)
114
114
end
115
115
116
116
function OnceDifferentiableConstraints (bounds:: ConstraintBounds )
117
- c! = (c,x)-> nothing
118
- J! = (J,x)-> nothing
117
+ c! = (c, x)-> nothing
118
+ J! = (J, x)-> nothing
119
119
OnceDifferentiableConstraints (c!, J!, bounds)
120
120
end
121
121
@@ -136,19 +136,19 @@ function OnceDifferentiableConstraints(c!, lx::AbstractVector, ux::AbstractVecto
136
136
sizex = size (lx)
137
137
sizec = size (lc)
138
138
139
- xcache = zeros (T,sizex)
140
- ccache = zeros (T,sizec)
139
+ xcache = zeros (T, sizex)
140
+ ccache = zeros (T, sizec)
141
141
142
- if any (autodiff .== ( :finite , :central ) )
142
+ if is_finitediff (autodiff)
143
143
ccache2 = similar (ccache)
144
- central_cache = FiniteDiff . JacobianCache (xcache, ccache,
145
- ccache2)
144
+ fdtype = finitediff_fdtype (autodiff)
145
+ jacobian_cache = FiniteDiff . JacobianCache (xcache, ccache, ccache2,fdtype )
146
146
function jfinite! (J, x)
147
- FiniteDiff. finite_difference_jacobian! (J, c!, x, central_cache )
147
+ FiniteDiff. finite_difference_jacobian! (J, c!, x, jacobian_cache )
148
148
J
149
149
end
150
150
return OnceDifferentiableConstraints (c!, jfinite!, bounds)
151
- elseif autodiff == :forward
151
+ elseif is_forwarddiff ( autodiff)
152
152
jac_cfg = ForwardDiff. JacobianConfig (c!, ccache, xcache, chunk)
153
153
ForwardDiff. checktag (jac_cfg, c!, xcache)
154
154
@@ -169,20 +169,167 @@ struct TwiceDifferentiableConstraints{F,J,H,T} <: AbstractConstraints
169
169
h!:: H # h!(storage, x) stores the hessian of the constraint functions
170
170
bounds:: ConstraintBounds{T}
171
171
end
172
+
172
173
function TwiceDifferentiableConstraints (c!, jacobian!, h!, lx, ux, lc, uc)
173
174
b = ConstraintBounds (lx, ux, lc, uc)
174
175
TwiceDifferentiableConstraints (c!, jacobian!, h!, b)
175
176
end
176
177
178
+ function TwiceDifferentiableConstraints (c!, lx:: AbstractVector , ux:: AbstractVector ,
179
+ lc:: AbstractVector , uc:: AbstractVector ,
180
+ autodiff:: Symbol = :central ,
181
+ chunk:: ForwardDiff.Chunk = checked_chunk (lx))
182
+ if is_finitediff (autodiff)
183
+ fdtype = finitediff_fdtype (autodiff)
184
+ return twicediff_constraints_finite (c!,lx,ux,lc,uc,fdtype,nothing )
185
+ elseif is_forwarddiff (autodiff)
186
+ return twicediff_constraints_forward (c!,lx,ux,lc,uc,chunk,nothing )
187
+ else
188
+ error (" The autodiff value $autodiff is not support. Use :finite or :forward." )
189
+ end
190
+ end
191
+
192
+ function TwiceDifferentiableConstraints (c!, con_jac!,lx:: AbstractVector , ux:: AbstractVector ,
193
+ lc:: AbstractVector , uc:: AbstractVector ,
194
+ autodiff:: Symbol = :central ,
195
+ chunk:: ForwardDiff.Chunk = checked_chunk (lx))
196
+ if is_finitediff (autodiff)
197
+ fdtype = finitediff_fdtype (autodiff)
198
+ return twicediff_constraints_finite (c!,lx,ux,lc,uc,fdtype,con_jac!)
199
+ elseif is_forwarddiff (autodiff)
200
+ return twicediff_constraints_forward (c!,lx,ux,lc,uc,chunk,con_jac!)
201
+ else
202
+ error (" The autodiff value $autodiff is not support. Use :finite or :forward." )
203
+ end
204
+ end
205
+
206
+
207
+
177
208
function TwiceDifferentiableConstraints (lx:: AbstractArray , ux:: AbstractArray )
178
209
bounds = ConstraintBounds (lx, ux, [], [])
179
210
TwiceDifferentiableConstraints (bounds)
180
211
end
181
212
213
+
214
+ function twicediff_constraints_forward (c!, lx, ux, lc, uc,chunk,con_jac! = nothing )
215
+ bounds = ConstraintBounds (lx, ux, lc, uc)
216
+ T = eltype (bounds)
217
+ nc = length (lc)
218
+ nx = length (lx)
219
+ ccache = zeros (T, nc)
220
+ xcache = zeros (T, nx)
221
+ cache_check = Ref {DataType} (Missing) # the datatype Missing, not the singleton
222
+ ref_f= Ref {Any} () # cache for intermediate jacobian used in the hessian
223
+ cxxcache = zeros (T, nx * nc, nx) # output cache for hessian
224
+ h = reshape (cxxcache, (nc, nx, nx)) # reshaped output
225
+ hi = [@view h[i,:,:] for i in 1 : nc]
226
+ # ref_f caches the closure function with its caches. other aproaches include using a Dict, but the
227
+ # cost of switching happens just once per optimize call.
228
+
229
+ if isnothing (con_jac!) # if the jacobian is not provided, generate one
230
+ jac_cfg = ForwardDiff. JacobianConfig (c!, ccache, xcache, chunk)
231
+ ForwardDiff. checktag (jac_cfg, c!, xcache)
232
+
233
+ jac! = (J, x) -> begin
234
+ ForwardDiff. jacobian! (J, c!, ccache, x, jac_cfg, Val {false} ())
235
+ J
236
+ end
237
+
238
+ con_jac_cached = x -> begin
239
+ exists_cache = (cache_check[] == eltype (x))
240
+ if exists_cache
241
+ f = ref_f[]
242
+ return f (x)
243
+ else
244
+ jcache = zeros (eltype (x), nc)
245
+ out_cache = zeros (eltype (x), nc, nx)
246
+ cfg_cache = ForwardDiff. JacobianConfig (c!,jcache,x)
247
+ f = z-> ForwardDiff. jacobian! (out_cache, c!, jcache, z,cfg_cache,Val {false} ())
248
+ ref_f[] = f
249
+ cache_check[]= eltype (x)
250
+ return f (x)
251
+ end
252
+ end
253
+
254
+ else
255
+ jac! = (J,x) -> con_jac! (J,x)
256
+
257
+ # here, the cache should also include a JacobianConfig
258
+ con_jac_cached = x -> begin
259
+ exists_cache = (cache_check[] == eltype (x))
260
+ if exists_cache
261
+ f = ref_f[]
262
+ return f (x)
263
+ else
264
+ out_cache = zeros (eltype (x), nc, nx)
265
+ f = z-> jac! (out_cache,x)
266
+ ref_f[] = f
267
+ cache_check[]= eltype (x)
268
+ return f (x)
269
+ end
270
+ end
271
+ end
272
+
273
+ hess_config_cache = ForwardDiff. JacobianConfig (typeof (con_jac_cached),lx)
274
+ function con_hess! (hess, x, λ)
275
+ ForwardDiff. jacobian! (cxxcache, con_jac_cached, x,hess_config_cache,Val {false} ())
276
+ for i = 1 : nc # hot hessian loop
277
+ hess+= λ[i]. * hi[i]
278
+ end
279
+ return hess
280
+ end
281
+
282
+ return TwiceDifferentiableConstraints (c!, jac!, con_hess!, bounds)
283
+ end
284
+
285
+
286
+ function twicediff_constraints_finite (c!,lx,ux,lc,uc,fdtype,con_jac! = nothing )
287
+ bounds = ConstraintBounds (lx, ux, lc, uc)
288
+ T = eltype (bounds)
289
+ nx = length (lx)
290
+ nc = length (lc)
291
+ xcache = zeros (T, nx)
292
+ ccache = zeros (T, nc)
293
+
294
+ if isnothing (con_jac!)
295
+ jac_ccache = similar (ccache)
296
+ jacobian_cache = FiniteDiff. JacobianCache (xcache, ccache,jac_ccache,fdtype)
297
+ function jac! (J, x)
298
+ FiniteDiff. finite_difference_jacobian! (J, c!, x, jacobian_cache)
299
+ J
300
+ end
301
+ else
302
+ jac! = (J,x) -> con_jac! (J,x)
303
+ end
304
+ cxxcache = zeros (T,nc* nx,nx) # to create cached jacobian
305
+ h = reshape (cxxcache, (nc, nx, nx)) # reshaped output
306
+ hi = [@view h[i,:,:] for i in 1 : nc]
307
+
308
+ function jac_vec! (J,x) # to evaluate the jacobian of a jacobian, FiniteDiff needs a vector version of that
309
+ j_mat = reshape (J,nc,nx)
310
+ return jac! (j_mat,x)
311
+ return J
312
+ end
313
+ hess_xcache = similar (xcache)
314
+ hess_cxcache = zeros (T,nc* nx) # output of jacobian, as a vector
315
+ hess_cxxcache = similar (hess_cxcache)
316
+ hess_config_cache = FiniteDiff. JacobianCache (hess_xcache,hess_cxcache,hess_cxxcache,fdtype)
317
+ function con_hess! (hess, x, λ)
318
+ FiniteDiff. finite_difference_jacobian! (cxxcache, jac_vec!, x,hess_config_cache)
319
+ for i = 1 : nc
320
+ hi = @view h[i,:,:]
321
+ hess+= λ[i]. * hi
322
+ end
323
+ return hess
324
+ end
325
+ return TwiceDifferentiableConstraints (c!, jac!, con_hess!, bounds)
326
+ end
327
+
328
+
182
329
function TwiceDifferentiableConstraints (bounds:: ConstraintBounds )
183
- c! = (x,c)-> nothing
184
- J! = (x,J)-> nothing
185
- h! = (x,λ, h)-> nothing
330
+ c! = (x, c)-> nothing
331
+ J! = (x, J)-> nothing
332
+ h! = (x, λ, h)-> nothing
186
333
TwiceDifferentiableConstraints (c!, J!, h!, bounds)
187
334
end
188
335
@@ -278,11 +425,11 @@ function showeq(io, indent, eq, val, chr, style)
278
425
if ! isempty (eq)
279
426
print (io, ' \n ' , indent)
280
427
if style == :bracket
281
- eqstrs = map ((i,v) -> " $chr [$i ]=$v " , eq, val)
428
+ eqstrs = map ((i, v) -> " $chr [$i ]=$v " , eq, val)
282
429
else
283
- eqstrs = map ((i,v) -> " $(chr) _$i =$v " , eq, val)
430
+ eqstrs = map ((i, v) -> " $(chr) _$i =$v " , eq, val)
284
431
end
285
- foreach (s-> print (io, s* " , " ), eqstrs[1 : end - 1 ])
432
+ foreach (s-> print (io, s * " , " ), eqstrs[1 : end - 1 ])
286
433
print (io, eqstrs[end ])
287
434
end
288
435
end
@@ -291,12 +438,12 @@ function showineq(io, indent, ineqs, σs, bs, chr, style)
291
438
if ! isempty (ineqs)
292
439
print (io, ' \n ' , indent)
293
440
if style == :bracket
294
- ineqstrs = map ((i,σ,b) -> " $chr [$i ]" * ineqstr (σ,b), ineqs, σs, bs)
441
+ ineqstrs = map ((i, σ, b) -> " $chr [$i ]" * ineqstr (σ, b), ineqs, σs, bs)
295
442
else
296
- ineqstrs = map ((i,σ,b) -> " $(chr) _$i " * ineqstr (σ,b), ineqs, σs, bs)
443
+ ineqstrs = map ((i, σ, b) -> " $(chr) _$i " * ineqstr (σ, b), ineqs, σs, bs)
297
444
end
298
- foreach (s-> print (io, s* " , " ), ineqstrs[1 : end - 1 ])
445
+ foreach (s-> print (io, s * " , " ), ineqstrs[1 : end - 1 ])
299
446
print (io, ineqstrs[end ])
300
447
end
301
448
end
302
- ineqstr (σ,b) = σ> 0 ? " ≥$b " : " ≤$b "
449
+ ineqstr (σ, b) = σ > 0 ? " ≥$b " : " ≤$b "
0 commit comments