Skip to content
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

[Utilities] add PenaltyRelaxation #1995

Merged
merged 20 commits into from
Nov 29, 2022
Merged

[Utilities] add PenaltyRelaxation #1995

merged 20 commits into from
Nov 29, 2022

Conversation

odow
Copy link
Member

@odow odow commented Sep 12, 2022

Part of jump-dev/JuMP.jl#3034. This PR explores the options we have to add a feasibility relaxation function to MOI.

The main decisions would be:

  • Should it modify in-place or create a new model?
  • What should we do for constraints which don't support in-place modification like variable bounds?

The other option, not implemented here, is some sort of Optimizer like Dualization.Optimizer. That might be a better solution, but would add more code and overhead.

@blegat
Copy link
Member

blegat commented Sep 12, 2022

That might be a better solution, but would add more code and overhead.

Not if we use MOI.Utilities.ModelFilter

@odow
Copy link
Member Author

odow commented Sep 29, 2022

Bump. Does anyone want to take a look at this?

@joaquimg
Copy link
Member

I like this and looks good. Any reason why is it not handling vector function-in-set's?

@odow
Copy link
Member Author

odow commented Sep 29, 2022

Any reason why is it not handling vector function-in-set's?

Only that it is a little more complicated to implement. I think this should cover 99% of the requests.

@mlubin
Copy link
Member

mlubin commented Oct 1, 2022

It seems weird conceptually to use attributes to (destructively) transform a model.

@blegat
Copy link
Member

blegat commented Oct 3, 2022

Indeed, maybe MOI.set(model, MOI.Utilities.FeasibilityRelaxation(Dict(c => 2.0))) could be MOI.modify(model, c, MOI.Utilities.FeasibilityRelaxation(2.0))

@odow
Copy link
Member Author

odow commented Oct 8, 2022

Yes, MOI.modify is a much better verb. I don't know why I didn't think of that.

@odow
Copy link
Member Author

odow commented Oct 17, 2022

Any comments now that I've swapped to MOI.modify?

@odow
Copy link
Member Author

odow commented Oct 27, 2022

  • Throw warning on unsupported constraints
  • Document that bounds should be rewritten as linear constraints
  • Potentially renamed to PenaltyRelaxation
  • Return a map between the constraint and the penalty
  • Consider FeasibilityRelaxation(dict; default) or some variation of

@odow odow changed the title [Utilities] add FeasibilityRelaxation [Utilities] add PenaltyRelaxation Oct 27, 2022
@odow
Copy link
Member Author

odow commented Oct 27, 2022

This works quite nicely. Here's what it looks like from JuMP:

julia> using JuMP, HiGHS

julia> model = Model(HiGHS.Optimizer);

julia> set_silent(model)

julia> @variable(model, x >= 0);

julia> @objective(model, Max, 2x + 1);

julia> @constraint(model, c, 2x - 1 <= -2);

julia> optimize!(model)

julia> function penalty_relaxation!(
           model::Model,
           penalties;
           default::Union{Nothing,Real} = 1.0,
       )
           if default !== nothing
               default = Float64(default)
           end
           moi_penalties = Dict{MOI.ConstraintIndex,Float64}(
               index(k) => Float64(v) for (k, v) in penalties
           )
           map = MOI.modify(
               backend(model),
               MOI.Utilities.PenaltyRelaxation(moi_penalties; default = default),
           )
           return Dict(
               ConstraintRef(model, k, ScalarShape()) => jump_function(model, v) for
               (k, v) in map
           )
       end
penalty_relaxation! (generic function with 2 methods)

julia> function penalty_relaxation!(model::Model; kwargs...)
           return penalty_relaxation!(model, Dict(); kwargs...)
       end
penalty_relaxation! (generic function with 2 methods)

julia> penalties = penalty_relaxation!(model; default = 2)
Dict{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, ScalarShape}, AffExpr} with 1 entry:
  c : 2 x - _[2]  -1.0 => _[2]

julia> print(model)
Max 2 x - 2 _[2] + 1
Subject to
 c : 2 x - _[2]  -1.0
 x  0.0
 _[2]  0.0

julia> optimize!(model)

julia> value(penalties[c])
1.0

@odow
Copy link
Member Author

odow commented Nov 15, 2022

Bump.

::MOI.ConstraintIndex,
::ScalarPenaltyRelaxation,
)
# A generic fallback if modification is not supported.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inconsistent, if a modification is not supported, an error should be thrown instead. Is that useful for VariableIndex only ? Then we can check whether it is VariableIndex before calling modify.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's for any set that isn't supported.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We either need supports or we need this fallback.

Copy link
Member

@blegat blegat Nov 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's for any set that isn't supported.

Isn't the method below defined for any AbstractScalarSet ?
So this is in case an MOI extension defines another type of AbstractScalarFunction ?
We could extend the definition of supports but then we need to add supports methods for solvers.

Another option is to add a keyword to the modify that is model-wise like ignore_unsupported.
If this is false, we just modify and let the error propagate.
Otherwise, we use a try-catch and handle ModifyConstraintNotAllowed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the try-catch.

Copy link
Member

@blegat blegat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably consider adding a default fallback throwing ModifyConstraintNotAllowed but then we should do it for all modifications so it's better done in a separate PR

@odow odow merged commit e75fff3 into master Nov 29, 2022
@odow odow deleted the od/feas-relax branch November 29, 2022 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants