-
Notifications
You must be signed in to change notification settings - Fork 5
Support cutting planes technique. #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 48 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
e8fb40f
WIP Multi-Big M
dnguyen227 0f7ad32
Deleting mbm copy.jl as it's not needed anymore
dnguyen227 c2e07a9
Added code coverage.
dnguyen227 2b25f70
More test cases
dnguyen227 dce303e
Testing MBM type
dnguyen227 5a1c0b2
working ver
dnguyen227 783d7af
Code coverage 100% for mbm.jl file.
dnguyen227 fdbe64c
Added comments, full code coverage on mbm.jl file TO BE TESTED
dnguyen227 db73d2f
Code coverage tested to be 100%. Further documentation for mbm te…
dnguyen227 0f40895
Delete src/codecoverage.jl
dnguyen227 0ae3623
Update datatypes.jl
dnguyen227 0044ea4
Updated documentation, JuMP. calls.
dnguyen227 56ca380
More JuMP. additions
dnguyen227 9830edb
Works with AbstractVariableRef
dnguyen227 2010800
Made _copy_variable function
dnguyen227 151abec
_copy_variable function added.
dnguyen227 10ca5d7
Updated _copy_variable()
dnguyen227 3b1d1ff
Update before deleting
dnguyen227 d022b3f
Merge branch 'cutting_planes' of https://github.com/dnguyen227/Disjun…
dnguyen227 89ce395
Initial working solution. To be tested.
dnguyen227 178a863
.
dnguyen227 965f997
Merge remote-tracking branch 'upstream/master' into cutting_planes
dnguyen227 10e3fd1
.
dnguyen227 1eedeac
addition of option for user to specify final reformulation technique
dnguyen227 6368e83
working copy
dnguyen227 a18839f
test file added.
dnguyen227 6b1ad17
working to fix nested disjunctions
dnguyen227 594eb50
solve.jl works
dnguyen227 3aa8b62
80 character limit
dnguyen227 49b4fad
.
dnguyen227 2a7cdd5
removed old code from constraints.jl
dnguyen227 af50c87
removed spacing edits.
dnguyen227 b032a54
.
dnguyen227 13a87cb
.
dnguyen227 efd644e
working version with tests
dnguyen227 4406ce6
Merge branch 'copy_model' into cutting_planes-with-modified-copy_model
dnguyen227 4c6fee4
Refactor variable handling and add model copying function
dnguyen227 d801563
Add tests for copy_model_and_gdp_data function
dnguyen227 9315da7
copy_model integratred into cutting planes. keyword arguements added
dnguyen227 f148cac
added remapping functions for specific gdpdata field.
dnguyen227 aab3ec4
added solving test in modeljl for copy_model
dnguyen227 668def9
Merge branch 'copy_model' into cutting_planes-with-modified-copy_model
dnguyen227 b486366
Merge remote-tracking branch 'upstream/master' into cutting_planes
dnguyen227 9041248
Updated tests.
dnguyen227 192b66a
update tests
dnguyen227 1f6c301
modified tests to check keyword arguments
dnguyen227 76d08b4
updated cutting_planes function with keyword arguements
dnguyen227 6a6cd70
update readme
dnguyen227 a1e92b5
additional comments.
dnguyen227 50acafe
more spacing fixed
dnguyen227 ddf9b44
modified tests
dnguyen227 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| function reformulate_model( | ||
| model::JuMP.AbstractModel, | ||
| method::cutting_planes | ||
| ) | ||
| _clear_reformulations(model) | ||
| var_type = JuMP.variable_ref_type(model) | ||
| obj = objective_function(model) | ||
| sense = objective_sense(model) | ||
| SEP, sep_ref_map, _ = copy_gdp_model(model) | ||
| rBM, rBM_ref_map, _ = copy_gdp_model(model) | ||
| reformulate_model(rBM, BigM(method.M_value)) | ||
| reformulate_model(SEP, Hull()) | ||
| main_to_SEP_map = Dict(v => sep_ref_map[v] for v in all_variables(model)) | ||
| main_to_rBM_map = Dict(v => rBM_ref_map[v] for v in all_variables(model)) | ||
| JuMP.set_optimizer(SEP, method.optimizer) | ||
| JuMP.set_optimizer(rBM, method.optimizer) | ||
| JuMP.@objective(rBM, sense, | ||
| _replace_variables_in_constraint(obj, main_to_rBM_map) | ||
| ) | ||
| for m in [SEP, rBM] | ||
| binary_vars = filter(is_binary, all_variables(m)) | ||
| for var in binary_vars | ||
| unset_binary(var) | ||
| set_lower_bound(var, 0.0) | ||
| set_upper_bound(var, 1.0) | ||
| end | ||
| end | ||
|
|
||
| rBM_to_SEP_map = Dict{var_type, var_type}() | ||
| SEP_to_rBM_map = Dict{var_type, var_type}() | ||
| for (var, rBM_var) in main_to_rBM_map | ||
| SEP_var = main_to_SEP_map[var] | ||
| rBM_to_SEP_map[rBM_var] = SEP_var | ||
| SEP_to_rBM_map[SEP_var] = rBM_var | ||
| end | ||
|
|
||
| i = 1 | ||
| sep_obj = 1 | ||
dnguyen227 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| while i <= method.max_iter && sep_obj > method.seperation_tolerance | ||
| rBM_sol = _solve_rBM(rBM) | ||
| SEP_sol = _solve_SEP(SEP, rBM, rBM_sol, SEP_to_rBM_map, rBM_to_SEP_map) | ||
| sep_obj = objective_value(SEP) | ||
| _cutting_planes(model, rBM, main_to_rBM_map, | ||
| main_to_SEP_map, rBM_sol, SEP_sol | ||
| ) | ||
| i += 1 | ||
| end | ||
| reformulate_model(model, method.final_reform_method) | ||
| return | ||
| end | ||
|
|
||
| function _solve_rBM( | ||
| rBM::M, | ||
| ) where {M <: JuMP.AbstractModel} | ||
| T = JuMP.value_type(M) | ||
| JuMP.set_silent(rBM) | ||
dnguyen227 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| optimize!(rBM, ignore_optimize_hook = true) | ||
| rBM_vars = JuMP.all_variables(rBM) | ||
| sol = Dict{JuMP.AbstractVariableRef, T}(var => zero(T) for var in rBM_vars) | ||
| for rBM_var in rBM_vars | ||
| sol[rBM_var] = JuMP.value(rBM_var) | ||
| end | ||
| return sol | ||
| end | ||
|
|
||
| function _solve_SEP( | ||
| SEP::M, | ||
| rBM::M, | ||
| rBM_sol::Dict{<:JuMP.AbstractVariableRef, T}, | ||
| SEP_to_rBM_map::Dict{<:JuMP.AbstractVariableRef, <:JuMP.AbstractVariableRef}, | ||
| rBM_to_SEP_map::Dict{<:JuMP.AbstractVariableRef, <:JuMP.AbstractVariableRef} | ||
| ) where {M <: JuMP.AbstractModel, T <: Number} | ||
| JuMP.set_silent(SEP) | ||
dnguyen227 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| SEP_vars = [rBM_to_SEP_map[rBM_var] for rBM_var in JuMP.all_variables(rBM)] | ||
| obj_expr = sum( | ||
| (SEP_var - rBM_sol[SEP_to_rBM_map[SEP_var]])^2 for SEP_var in SEP_vars | ||
| ) | ||
dnguyen227 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| JuMP.@objective(SEP, Min, obj_expr) | ||
|
|
||
| optimize!(SEP, ignore_optimize_hook = true) | ||
|
|
||
| sol = Dict{JuMP.AbstractVariableRef, T}(var => zero(T) for var in SEP_vars) | ||
| for SEP_var in SEP_vars | ||
| sol[SEP_var] = JuMP.value(SEP_var) | ||
| end | ||
| return sol | ||
| end | ||
|
|
||
| function _cutting_planes( | ||
| model::M, | ||
| rBM::M, | ||
| main_to_rBM_map::Dict{<:JuMP.AbstractVariableRef, <:JuMP.AbstractVariableRef}, | ||
| main_to_SEP_map::Dict{<:JuMP.AbstractVariableRef, <:JuMP.AbstractVariableRef}, | ||
| rBM_sol::Dict{<:JuMP.AbstractVariableRef, T}, | ||
| SEP_sol::Dict{<:JuMP.AbstractVariableRef, T}, | ||
| ) where {M <: JuMP.AbstractModel, T <: Number} | ||
| main_vars = JuMP.all_variables(model) | ||
|
|
||
| ξ_sep = Dict{JuMP.AbstractVariableRef, T}(var => zero(T) for var in main_vars) | ||
| for var in main_vars | ||
| ξ_sep[var] = 2*(SEP_sol[main_to_SEP_map[var]] - rBM_sol[main_to_rBM_map[var]]) | ||
| end | ||
| main_cut = JuMP.@expression(model, | ||
| sum(ξ_sep[var]*(var - SEP_sol[main_to_SEP_map[var]]) for var in main_vars) | ||
dnguyen227 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) | ||
| rBM_cut = _replace_variables_in_constraint(main_cut, main_to_rBM_map) | ||
| JuMP.@constraint(model, main_cut >= 0.0) | ||
| JuMP.@constraint(rBM, rBM_cut >= 0.0) | ||
| end | ||
|
|
||
| ################################################################################ | ||
| # ERROR MESSAGES | ||
| ################################################################################ | ||
|
|
||
| function reformulate_model(::M, ::cutting_planes) where {M} | ||
| error("reformulate_model not implemented for model type `$(M)`.") | ||
| end | ||
|
|
||
| function _solve_rBM(::M) where {M} | ||
| error("_solve_rBM not implemented for model type `$(M)`.") | ||
| end | ||
|
|
||
| function _solve_SEP(::M, ::N, ::H, ::S, ::R) where {M, N, H, S, R} | ||
| error("_solve_SEP not implemented for argument types:\n | ||
| SEP: `$(M)`, rBM: `$(N)`,\n | ||
| rBM_sol: `$(H)`,\n | ||
| SEP_to_rBM_map: `$(S)`,\n | ||
| rBM_to_SEP_map: `$(R)`.") | ||
| end | ||
|
|
||
| function _cutting_planes(::M, ::N, ::H, ::S, ::R, ::T) where {M, N, H, S, R, T} | ||
| error("_cutting_planes not implemented for argument types: \n | ||
| model: `$(M)`, rBM: `$(N)`,\n | ||
| main_to_rBM_map: `$(H)`, main_to_SEP_map: | ||
| `$(S)`,\n | ||
| rBM_sol: `$(R)`,\n | ||
| SEP_sol: `$(T)`.") | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| using HiGHS | ||
|
|
||
| function test_cutting_planes_datatype() | ||
| method = cutting_planes(HiGHS.Optimizer) | ||
| @test method.optimizer == HiGHS.Optimizer | ||
| @test method.max_iter == 3 | ||
| @test method.seperation_tolerance == 1e-6 | ||
| @test method.final_reform_method isa BigM | ||
| @test method.M_value == 1e9 | ||
|
|
||
| method = cutting_planes(HiGHS.Optimizer;max_iter=10, | ||
| seperation_tolerance=1e-4, final_reform_method=Indicator(), M_value=1e6 | ||
| ) | ||
| @test method.max_iter == 10 | ||
| @test method.seperation_tolerance == 1e-4 | ||
| @test method.final_reform_method isa Indicator | ||
| @test method.M_value == 1e6 | ||
| end | ||
|
|
||
| function test_solve_rBM() | ||
| rBM = JuMP.Model(HiGHS.Optimizer) | ||
| @variable(rBM, 0 <= x <= 100) | ||
| @variable(rBM, 0 <= y[1:2] <= 1) | ||
| @constraint(rBM, x <= 3 + 100(1 - y[1])) | ||
| @constraint(rBM, x <= 4 + 100(1 - y[2])) | ||
| @constraint(rBM, y[1] + y[2] == 1) | ||
| @objective(rBM, Max, x) | ||
|
|
||
| solutions = DP._solve_rBM(rBM) | ||
| @test solutions[x] == 53.5 | ||
| @test solutions[y[1]] == 0.495 | ||
| @test solutions[y[2]] == 0.505 | ||
|
|
||
| @test_throws ErrorException DP._solve_rBM(Dict()) | ||
| end | ||
|
|
||
| function test_solve_SEP() | ||
| model = GDPModel() | ||
| @variable(model, 0 <= x <= 100) | ||
| @variable(model, Y[1:2], Logical) | ||
| @constraint(model, x <= 3, Disjunct(Y[1])) | ||
| @constraint(model, x <= 4, Disjunct(Y[2])) | ||
| @disjunction(model, [Y[1], Y[2]]) | ||
| @objective(model, Max, x) | ||
| var_type = JuMP.variable_ref_type(model) | ||
| method = cutting_planes(HiGHS.Optimizer) | ||
| obj = objective_function(model) | ||
| sense = objective_sense(model) | ||
| SEP, sep_ref_map, _ = DP.copy_gdp_model(model) | ||
| rBM, rBM_ref_map, _ = DP.copy_gdp_model(model) | ||
| DP.reformulate_model(rBM, DP.BigM(method.M_value)) | ||
| DP.reformulate_model(SEP, DP.Hull()) | ||
| main_to_SEP_map = Dict(v => sep_ref_map[v] for v in all_variables(model)) | ||
| main_to_rBM_map = Dict(v => rBM_ref_map[v] for v in all_variables(model)) | ||
| JuMP.set_optimizer(SEP, method.optimizer) | ||
| JuMP.set_optimizer(rBM, method.optimizer) | ||
| JuMP.@objective(rBM, sense, | ||
| DP._replace_variables_in_constraint(obj, main_to_rBM_map) | ||
| ) | ||
| for m in [SEP, rBM] | ||
| binary_vars = filter(is_binary, all_variables(m)) | ||
| for var in binary_vars | ||
| unset_binary(var) | ||
| set_lower_bound(var, 0.0) | ||
| set_upper_bound(var, 1.0) | ||
| end | ||
| end | ||
| rBM_to_SEP_map = Dict{var_type, var_type}() | ||
| SEP_to_rBM_map = Dict{var_type, var_type}() | ||
| for (var, rBM_var) in main_to_rBM_map | ||
| SEP_var = main_to_SEP_map[var] | ||
| rBM_to_SEP_map[rBM_var] = SEP_var | ||
| SEP_to_rBM_map[SEP_var] = rBM_var | ||
| end | ||
| rBM_sol = DP._solve_rBM(rBM) | ||
| SEP_sol = DP._solve_SEP(SEP, rBM, rBM_sol, SEP_to_rBM_map, rBM_to_SEP_map) | ||
| @test length(SEP_sol) == length(rBM_sol) | ||
| @test SEP_sol[rBM_to_SEP_map[main_to_rBM_map[x]]] ≈ 4.0 | ||
|
|
||
| @test_throws ErrorException DP._solve_SEP(SEP, rBM, rBM_sol, SEP_to_rBM_map, "not a dict") | ||
| end | ||
|
|
||
| function test_cutting_planes() | ||
| model = GDPModel() | ||
| @variable(model, 0 <= x <= 100) | ||
| @variable(model, Y[1:2], Logical) | ||
| @constraint(model, x <= 3, Disjunct(Y[1])) | ||
| @constraint(model, x <= 4, Disjunct(Y[2])) | ||
| @disjunction(model, [Y[1], Y[2]]) | ||
| @objective(model, Max, x) | ||
| var_type = JuMP.variable_ref_type(model) | ||
| method = cutting_planes(HiGHS.Optimizer) | ||
| obj = objective_function(model) | ||
| sense = objective_sense(model) | ||
| SEP, sep_ref_map, _ = DP.copy_gdp_model(model) | ||
| rBM, rBM_ref_map, _ = DP.copy_gdp_model(model) | ||
| DP.reformulate_model(rBM, DP.BigM(method.M_value)) | ||
| DP.reformulate_model(SEP, DP.Hull()) | ||
| main_to_SEP_map = Dict(v => sep_ref_map[v] for v in all_variables(model)) | ||
| main_to_rBM_map = Dict(v => rBM_ref_map[v] for v in all_variables(model)) | ||
| JuMP.set_optimizer(SEP, method.optimizer) | ||
| JuMP.set_optimizer(rBM, method.optimizer) | ||
| JuMP.@objective(rBM, sense, | ||
| DP._replace_variables_in_constraint(obj, main_to_rBM_map) | ||
| ) | ||
| for m in [SEP, rBM] | ||
| binary_vars = filter(is_binary, all_variables(m)) | ||
| for var in binary_vars | ||
| unset_binary(var) | ||
| set_lower_bound(var, 0.0) | ||
| set_upper_bound(var, 1.0) | ||
| end | ||
| end | ||
| rBM_to_SEP_map = Dict{var_type, var_type}() | ||
| SEP_to_rBM_map = Dict{var_type, var_type}() | ||
| for (var, rBM_var) in main_to_rBM_map | ||
| SEP_var = main_to_SEP_map[var] | ||
| rBM_to_SEP_map[rBM_var] = SEP_var | ||
| SEP_to_rBM_map[SEP_var] = rBM_var | ||
| end | ||
| rBM_sol = DP._solve_rBM(rBM) | ||
| SEP_sol = DP._solve_SEP(SEP, rBM, rBM_sol, SEP_to_rBM_map, rBM_to_SEP_map) | ||
| DP._cutting_planes(model, rBM, main_to_rBM_map, main_to_SEP_map, rBM_sol, SEP_sol) | ||
|
|
||
| rBM_sol = DP._solve_rBM(rBM) | ||
| SEP_sol = DP._solve_SEP(SEP, rBM, rBM_sol, SEP_to_rBM_map, rBM_to_SEP_map) | ||
|
|
||
| @test rBM_sol[main_to_rBM_map[x]] ≈ 4.0 | ||
| @test SEP_sol[rBM_to_SEP_map[main_to_rBM_map[x]]] ≈ 4.0 atol=1e-3 | ||
|
|
||
| @test_throws ErrorException DP._cutting_planes(model, rBM, main_to_rBM_map, main_to_SEP_map, rBM_sol, "not a dict") | ||
| end | ||
|
|
||
| function test_reformulate_model() | ||
| model = GDPModel() | ||
| @variable(model, 0 <= x[1:4] <= 100) | ||
| @variable(model, Y[1:2], Logical) | ||
| @constraint(model, x[1] + x[2] <= 3, Disjunct(Y[1])) | ||
| @constraint(model, x[3] + x[4] <= 4, Disjunct(Y[2])) | ||
| @disjunction(model, [Y[1], Y[2]]) | ||
| @objective(model, Max, x[1] + x[2]) | ||
|
|
||
| method = cutting_planes(HiGHS.Optimizer) | ||
| DP.reformulate_model(model, method) | ||
| num_con = length( | ||
| JuMP.all_constraints(model; include_variable_in_set_constraints = false) | ||
| ) | ||
| @test num_con == 4 | ||
| @test_throws ErrorException DP.reformulate_model(42, method) | ||
| end | ||
|
|
||
|
|
||
| @testset "Cutting Planes" begin | ||
| test_cutting_planes_datatype() | ||
| test_solve_rBM() | ||
| test_solve_SEP() | ||
| test_cutting_planes() | ||
| test_reformulate_model() | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.