Skip to content

Commit e1b5a04

Browse files
committed
Add warmstart_backend_start_values
1 parent ed532b7 commit e1b5a04

File tree

9 files changed

+124
-1
lines changed

9 files changed

+124
-1
lines changed

docs/src/develop/extensions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,7 @@ extended using the following steps:
779779
8. As appropriate and if NOT a `JuMPBackend`, extend the following:
780780
- The remaining result related attributes listed in [`JuMP.get_attribute`](@ref JuMP.get_attribute(::AbstractTransformationBackend, ::Any))
781781
- [`JuMP.lp_sensitivity_report`](@ref JuMP.lp_sensitivity_report(::AbstractTransformationBackend))
782+
- [`InfiniteOpt.warmstart_backend_start_values`](@ref) to enable automated warmstarts
782783
9. If Step 6 was skipped and/or the backend is NOT a `JuMPBackend` then extend the following:
783784
- [`InfiniteOpt.map_value`](@ref) (enables `JuMP.value`)
784785
- [`InfiniteOpt.map_infinite_parameter_value`](@ref) (enables `JuMP.value` for infinite parameters)

docs/src/guide/optimize.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ via [`set_optimizer`](@ref):
6363
julia> set_optimizer(model, Ipopt.Optimizer)
6464
```
6565

66+
!!! note
67+
For effective resolves,
68+
[`warmstart_backend_start_values`](@ref warmstart_backend_start_values(::InfiniteModel))
69+
provides a convenient and efficient way to update the start values used by
70+
the backend using a previous solution stored in the backend.
71+
```julia
72+
optimize!(model)
73+
warmstart_backend_start_values(model)
74+
set_parameter_value(p, 42)
75+
optimize!(model)
76+
```
77+
6678
A number of methods also exist to adjust the optimizer settings such as
6779
suppressing output. This is explained below in the
6880
[Optimizer Settings](@ref opt_settings) section.

docs/src/guide/variable.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,18 @@ julia> start_value(z)
890890
0.0
891891
```
892892

893+
!!! note
894+
For effective resolves,
895+
[`warmstart_backend_start_values`](@ref warmstart_backend_start_values(::InfiniteModel))
896+
provides a convenient and efficient way to update the start values used by
897+
the backend using a previous solution stored in the backend.
898+
```julia
899+
optimize!(model)
900+
warmstart_backend_start_values(model)
901+
set_parameter_value(p, 42)
902+
optimize!(model)
903+
```
904+
893905
For infinite variables, this should be done using
894906
[`set_start_value_function`](@ref). FOr example:
895907
```jldoctest var_macro
@@ -903,4 +915,4 @@ arguments that exactly match the format of the infinite parameters given in
903915
`Infinite(params...)`.
904916

905917
A number of other techniques exist for the various variable types can be found in
906-
the manual below.
918+
the manual.

docs/src/manual/backend.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ constraint_supports(::InfOptConstraintRef, ::AbstractTransformationBackend)
7373
transformation_backend_ready
7474
set_transformation_backend_ready
7575
update_parameter_value(::AbstractTransformationBackend, ::Any, ::Any)
76+
warmstart_backend_start_values(::AbstractTransformationBackend)
7677
```

docs/src/manual/result.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ JuMP.lp_sensitivity_report(::InfiniteModel)
5656
InfOptSensitivityReport
5757
```
5858

59+
## Warmstarts
60+
```@docs
61+
warmstart_backend_start_values(::InfiniteModel)
62+
```
63+
5964
## Transformation Backend Extension API
6065
```@docs
6166
map_value(::Any, ::AbstractTransformationBackend)

src/backends.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,48 @@ function build_transformation_backend!(model::InfiniteModel; kwargs...)
339339
return
340340
end
341341

342+
"""
343+
warmstart_backend_start_values(
344+
backend::AbstractTransformationBackend;
345+
[kwargs...]
346+
)::Nothing
347+
348+
Use the previous solution values stored in `backend` to warmstart the start values
349+
to be used for the next [`optimize!`](@ref) call. This serves as an extension point
350+
for new backend types.
351+
"""
352+
function warmstart_backend_start_values(
353+
backend::AbstractTransformationBackend;
354+
kwargs...
355+
)
356+
error("`warmstart_backend_start_values` not implemented for transformation backends " *
357+
"of type `$(typeof(backend))`.")
358+
end
359+
360+
"""
361+
warmstart_backend_start_values(model::InfiniteModel; [kwargs...])
362+
363+
Use the previous solution values (primals and duals) stored in the transformation
364+
backend of `model` to warmstart the start values to be used for the next
365+
[`optimize!`](@ref) call. For `JuMPBackend`s (like `TranscriptionBackend`) this calls
366+
`JuMP.set_start_values` on the underlying JuMP model. Note that only start values in
367+
the backend model are updated, the start values stored in `model` remain unchanged.
368+
369+
**Example*
370+
```julia-repl
371+
julia> optimize!(model)
372+
373+
julia> warmstart_backend_start_values(model) # call before making model updates
374+
375+
julia> set_parameter_value(p, 42)
376+
377+
julia> optimize!(model)
378+
```
379+
"""
380+
function warmstart_backend_start_values(model::InfiniteModel; kwargs...)
381+
return warmstart_backend_start_values(model.backend; kwargs...)
382+
end
383+
342384
"""
343385
JuMP.set_optimize_hook(
344386
model::InfiniteModel,
@@ -696,6 +738,19 @@ end
696738
function JuMP.optimize!(backend::JuMPBackend)
697739
return JuMP.optimize!(backend.model)
698740
end
741+
function warmstart_backend_start_values(backend::JuMPBackend; kwargs...)
742+
jump_model = transformation_model(backend)
743+
if JuMP.has_values(jump_model)
744+
JuMP.set_start_values(
745+
jump_model;
746+
nonlinear_dual_start = nothing,
747+
kwargs...
748+
)
749+
else
750+
@warn("No previous solution values found to warmstart the backend.")
751+
end
752+
return
753+
end
699754

700755
################################################################################
701756
# VARIABLE MAPPING API

test/extensions.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ end
262262

263263
# prepare for result queries
264264
mockoptimizer = JuMP.backend(m).optimizer.model
265+
for v in all_variables(m.backend.model)
266+
MOI.set(mockoptimizer, MOI.VariablePrimal(),
267+
JuMP.optimizer_index(v), 1.0)
268+
end
265269
MOI.set(mockoptimizer, MOI.TerminationStatus(), MOI.OPTIMAL)
266270
MOI.set(mockoptimizer, MOI.RawStatusString(), "solver specific string")
267271
MOI.set(mockoptimizer, MOI.ResultCount(), 2)
@@ -315,6 +319,8 @@ end
315319
@test dual(c2) == [0., -1.]
316320
@test dual(c3) == [0., -1.]
317321
@test is_solved_and_feasible(m)
322+
@test warmstart_backend_start_values(m) isa Nothing
323+
@test start_value(transformation_variable(y)) == 1
318324
# @test optimizer_index(x) == optimizer_index.(transformation_variable(x))
319325
# @test optimizer_index(x0) == optimizer_index(transformation_variable(x0))
320326
# @test optimizer_index(y) == optimizer_index(transformation_variable(y))

test/extensions/backend.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,17 @@ function InfiniteOpt.update_parameter_value(
405405
end
406406
return true
407407
end
408+
409+
# Extend `warmstart_backend_start_values` to support warmstarting
410+
function InfiniteOpt.warmstart_backend_start_values(
411+
backend::NewReformBackend;
412+
my_kwarg = true # EXTRA CAN BE PASSED ON TO THE MAPPING
413+
)
414+
# REPLACE BELOW WITH ACTUAL WARMSTARTING OPERATION
415+
if my_kwarg
416+
JuMP.set_start_values(transformation_model(backend))
417+
else
418+
@warn("Unable to warmstart backend.")
419+
end
420+
return
421+
end

test/results.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ end
203203
MOI.set(mockoptimizer, MOI.ConstraintDual(), JuMP.optimizer_index(c), -12.0)
204204
end
205205
end
206+
for v in all_variables(tb.model)
207+
MOI.set(mockoptimizer, MOI.VariablePrimal(1), JuMP.optimizer_index(v), 1.0)
208+
end
206209
# MOI.set(mockoptimizer, MOI.ConstraintDual(), JuMP.optimizer_index(creft), 7.0)
207210
MOI.set(mockoptimizer, MOI.VariablePrimal(1), JuMP.optimizer_index(gt), 1.0)
208211
MOI.set(mockoptimizer, MOI.VariablePrimal(1), JuMP.optimizer_index(inft[1]), 2.0)
@@ -270,6 +273,11 @@ end
270273
@test isa(optimizer_index(inf, label = InternalLabel), Vector{MOI.VariableIndex})
271274
@test isa(optimizer_index(rv), Vector{MOI.VariableIndex})
272275
end
276+
# test warmstart_backend_start_values
277+
@testset "warmstart_backend_start_values" begin
278+
@test warmstart_backend_start_values(m) isa Nothing
279+
@test start_value(gt) == 1
280+
end
273281
# test dual
274282
@testset "JuMP.dual" begin
275283
@test_throws ErrorException dual(g)
@@ -436,12 +444,21 @@ end
436444
@test shadow_price(c4) == [-2, -3]
437445
@test_throws ErrorException InfiniteOpt.map_shadow_price(c1, TestBackend())
438446
end
447+
# test warmstart_backend_start_values
448+
@testset "warmstart_backend_start_values" begin
449+
@test warmstart_backend_start_values(m) isa Nothing
450+
@test start_value(c1t) == 1
451+
@test dual_start_value(c1t) == -1
452+
@test_throws ErrorException warmstart_backend_start_values(TestBackend())
453+
end
439454
# test model not up to date
440455
set_objective_sense(m, MOI.MAX_SENSE)
441456
@testset "Not up-to-date" begin
442457
@test_throws ErrorException dual(c1)
443458
@test_throws ErrorException shadow_price(c1)
444459
@test_throws ErrorException optimizer_index(c1)
460+
str = "No previous solution values found to warmstart the backend."
461+
@test_logs (:warn, str) warmstart_backend_start_values(m)
445462
end
446463
end
447464

0 commit comments

Comments
 (0)