-
Notifications
You must be signed in to change notification settings - Fork 27
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
Consider variable bounds clean #191
base: master
Are you sure you want to change the base?
Changes from 2 commits
a1e9b14
f6a4dfd
a38a298
8ce6d92
47f27ff
86c04ed
44c74e0
422b406
ed21f79
4b4e98f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -255,6 +255,52 @@ end | |
accept_vector_set(::ProductMode{T}, ::Complement) where T = nothing | ||
accept_vector_set(::MixedMode{T}, ::Complement) where T = nothing | ||
|
||
function get_variable_complement(primal_model, dual_model, primal_con, dual_con) | ||
error("An internal error with variable complements occurred. Likely, your problem type does not yet support consideration of constrained variables.") | ||
end | ||
|
||
function get_variable_complement(primal_model, dual_model, primal_con::MOI.ConstraintIndex{Fp,Sp}, dual_con::MOI.ConstraintIndex{Fd,Sd}) where {Fp<:MOI.VariableIndex,Sp<:MOI.GreaterThan{T},Fd,Sd<:MOI.GreaterThan{T}} where T | ||
primal_variable = MOI.get(primal_model, MOI.ConstraintFunction(), primal_con) | ||
primal_set = MOI.get(primal_model, MOI.ConstraintSet(), primal_con) | ||
|
||
@assert MOI.constant(primal_set) == 0 "Unexpected variable bound" | ||
|
||
dual_func = MOI.get(dual_model, MOI.ConstraintFunction(), dual_con) | ||
dual_set = MOI.get(dual_model, MOI.ConstraintSet(), dual_con) | ||
if MOI.constant(dual_set) > 0 | ||
dual_func.constant = dual_func.constant - MOI.constant(dual_set) | ||
elseif MOI.constant(dual_set) < 0 | ||
dual_func.constant = dual_func.constant + MOI.constant(dual_set) | ||
end | ||
return Complement(false, primal_con, dual_func, set_with_zero(dual_set), primal_variable) | ||
end | ||
|
||
function get_variable_complement(primal_model, dual_model, primal_con::MOI.ConstraintIndex{Fp,Sp}, dual_con::MOI.ConstraintIndex{Fd,Sd}) where {Fp<:MOI.VariableIndex,Sp<:MOI.LessThan{T},Fd,Sd<:MOI.LessThan{T}} where T | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these two functions look the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this was an old relict as I wasn't sure about constants etc in the beginning. Now, one is deleted and the other is defined for union of lesser/greater. Equalities still throw an error. |
||
primal_variable = MOI.get(primal_model, MOI.ConstraintFunction(), primal_con) | ||
primal_set = MOI.get(primal_model, MOI.ConstraintSet(), primal_con) | ||
|
||
@assert MOI.constant(primal_set) == 0 "Unexpected variable bound" | ||
|
||
dual_func = MOI.get(dual_model, MOI.ConstraintFunction(), dual_con) | ||
dual_set = MOI.get(dual_model, MOI.ConstraintSet(), dual_con) | ||
if MOI.constant(dual_set) > 0 | ||
dual_func.constant = dual_func.constant - MOI.constant(dual_set) | ||
elseif MOI.constant(dual_set) < 0 | ||
dual_func.constant = dual_func.constant + MOI.constant(dual_set) | ||
end | ||
return Complement(false, primal_con, dual_func, set_with_zero(dual_set), primal_variable) | ||
end | ||
|
||
function get_variable_complements(primal_model, dual_model, primal_dual_map) | ||
map = primal_dual_map.primal_con_dual_var | ||
out = Complement[] | ||
for (primal_con, dual_con) in primal_dual_map.constrained_var_dual | ||
con = get_variable_complement(primal_model, dual_model, primal_con, dual_con) | ||
push!(out, con) | ||
end | ||
return out | ||
end | ||
|
||
function get_canonical_complements(primal_model, primal_dual_map) | ||
map = primal_dual_map.primal_con_dual_var | ||
out = Complement[] | ||
|
@@ -311,7 +357,8 @@ function build_bilevel( | |
mode, | ||
upper_var_to_lower_ctr::Dict{VI,CI} = Dict{VI,CI}(); | ||
copy_names::Bool = false, | ||
pass_start::Bool = false | ||
pass_start::Bool = false, | ||
consider_constrained_variables::Bool = false, | ||
) | ||
|
||
# Start with an empty problem | ||
|
@@ -326,7 +373,7 @@ function build_bilevel( | |
dual_names = DualNames("dual_","dual_"), | ||
variable_parameters = upper_variables, | ||
ignore_objective = ignore_dual_objective(mode), | ||
consider_constrained_variables = false, | ||
consider_constrained_variables = consider_constrained_variables, | ||
) | ||
# the model | ||
lower_dual = dual_problem.dual_model | ||
|
@@ -428,8 +475,32 @@ function build_bilevel( | |
# feasible equality constraints always satisfy complementarity | ||
end | ||
end | ||
|
||
if consider_constrained_variables | ||
# complementary slackness for variable bounds | ||
variable_comps = get_variable_complements(lower, lower_dual, lower_primal_dual_map) | ||
for comp in variable_comps | ||
if !is_equality(comp.set_w_zero) | ||
accept_vector_set(mode, comp) | ||
add_complement( | ||
mode, | ||
m, | ||
comp, | ||
lower_dual_idxmap, | ||
lower_idxmap, | ||
copy_names, | ||
pass_start, | ||
) | ||
end | ||
end | ||
end | ||
|
||
else # strong duality | ||
add_strong_duality(mode, m, lower_primal_obj, lower_dual_obj, lower_idxmap, lower_dual_idxmap) | ||
if !consider_constrained_variables || contains_only_scalar_sets(lower) | ||
add_strong_duality(mode, m, lower_primal_obj, lower_dual_obj, lower_idxmap, lower_dual_idxmap) | ||
else | ||
error("Only scalar sets in lower level allowed with consider_constrained_variables.") | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont think any of this is needed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. true, this threw occasional errors earlier. Found out that this was related to missing dual variables for which start values could not be passed. |
||
end | ||
add_aggregate_constraints(m, mode, copy_names) | ||
|
||
|
@@ -1022,6 +1093,15 @@ function add_complement(mode::FortunyAmatMcCarlMode{T}, m, comp::Complement, | |
return c1 | ||
end | ||
|
||
function contains_only_scalar_sets(model::MOI.ModelLike) | ||
for (F,S) in MOI.get(model, MOI.ListOfConstraintTypesPresent()) | ||
if !(S <: SCALAR_SETS) | ||
return false | ||
end | ||
end | ||
return true | ||
end | ||
|
||
function to_vector_affine(f::MOI.VectorAffineFunction{T}) where T | ||
return f | ||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason for not supporting other sets like vector sets?
you can follow the code in get_canonical_complement, as you have been doing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left this out because I did not really feel certain.
Now added, can you check if it is correct?
Also not sure about the todo (see code for the new function)