-
Notifications
You must be signed in to change notification settings - Fork 87
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
Variable deletion with bridges #2153
Comments
Replying to #2156 (comment)
This is not about measuring benefit, it's about correctness, deleting a variable should have the same effect that rebuilding a model from scratch without that variable. This is currently the only exception I know. I'm not saying it should be fixed right away but we should keep this open at least |
Base MOI doesn't delete rows. And the internal workings of a bridge are private. We don't make any claims about efficiency etc. just that the transformation is correct. I don't think users could reasonably expect us to rebuild the transformation when we delete arbitrary variables. In theory, the user shouldn't be checking the inner bridged model, so they'd never know about the zero row. And even if they did, I'm not sure the extra complication in the bridge is worth the trade-off. julia> import MathOptInterface as MOI
julia> model = MOI.Utilities.Model{Float64}()
MOIU.Model{Float64}
julia> x = MOI.add_variables(model, 2)
2-element Vector{MathOptInterface.VariableIndex}:
MOI.VariableIndex(1)
MOI.VariableIndex(2)
julia> f = MOI.Utilities.operate(vcat, Float64, (1.0 .* x)...)
┌ ┐
│0.0 + 1.0 MOI.VariableIndex(1)│
│0.0 + 1.0 MOI.VariableIndex(2)│
└ ┘
julia> MOI.add_constraint(model, f, MOI.Nonnegatives(2))
MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Nonnegatives}(1)
julia> MOI.delete(model, x[1])
julia> print(model)
Feasibility
Subject to:
VectorAffineFunction{Float64}-in-Nonnegatives
┌ ┐
│0.0 │
│0.0 + 1.0 v[2]│
└ ┘ ∈ Nonnegatives(2) |
Some conic solvers might not behave so well with zero rows on the SOC constraints, as this constraint is indeed private to the bridge, the bridge is responsible to do the right thing in case of deletion. Throwing a |
This might be a good motivation. |
So I've been looking into this. It's quite complicated. We'd need to add a Isn't the alternative the solver throwing an error if it deletes a variable that ends up creating a zero row, and the solver doesn't support zero rows? Why does this need to be resolved in the bridge? |
How would that error be useful for the user ? He didn't create the SOC constraint, the bridge is responsible. |
The solver would throw
This feels complicated for something that we don't actually know is an issue yet. Can we wait for someone to complain? |
Conic solvers don't support deletion (except Mosek), it's done in their cache. It's pretty hard to catch down the line.
Yes but I'd prefer we leave this open. |
Why does the bridge need to be responsible for deciding whether deletion is allowed here? The inner optimizer can throw SCIP is an example of a solver that does this: I guess there might be I still think that this isn't an issue that will ever be a problem in practice, and I don't want to add complexity to MOI to deal with it. I think the sticking point is:
We have never made this claim, and it isn't true in the current implementation. |
Suppose you solve a QP, the problem is long to solve because you have a lot of variables. You delete most of them, you solve again, it still long to solver, what's happening ??? Users could also be implemented other bridges that would change if variables are deleted even though they act on quadratic/affine/nl functions. Not having this function in the API and in the docs would simply lead them to incorrectness without warning which isn't a good design. |
Can you find an example that demonstrates the problem? julia> using JuMP, ECOS
julia> function main(optimizer)
model = Model(optimizer)
set_silent(model)
@variable(model, x[1:2])
@objective(model, Min, sum(x.^2))
optimize!(model)
print(backend(model).optimizer.model)
delete(model, x[2])
optimize!(model)
print(backend(model).optimizer.model)
end
main (generic function with 1 method)
julia> main(ECOS.Optimizer)
Minimize ScalarAffineFunction{Float64}:
0.0 + 1.0 v[3]
Subject to:
VectorAffineFunction{Float64}-in-SecondOrderCone
┌ ┐
│0.7071067811865475 + 0.7071067811865475 v[3]│
│0.7071067811865475 - 0.7071067811865475 v[3]│
│0.0 + 1.4142135623730951 x[1] │
│0.0 + 1.4142135623730951 x[2] │
└ ┘ ∈ SecondOrderCone(4)
Minimize ScalarAffineFunction{Float64}:
0.0 + 1.0 v[2]
Subject to:
VectorAffineFunction{Float64}-in-SecondOrderCone
┌ ┐
│0.7071067811865475 + 0.7071067811865475 v[2]│
│0.7071067811865475 - 0.7071067811865475 v[2]│
│0.0 + 1.4142135623730951 x[1] │
└ ┘ ∈ SecondOrderCone(3) |
ECOS doesn't support variable deletion so that won't be an issue with ECOS |
So which solver supports variable deletions and has a problem with |
It's not about likelihood, a bug is a bug :) We don't know about all solvers or all bridges that exists. If a bug can occur with the current design of bridges with a bridge we have a possibly other bridge when a solver support deletion then the design should be fixed. |
I still don't see how this is a bug, but rather less than perfect behavior. Can we have a concrete example of a solver and bridge where this is a problem? |
All bridges in MOI are affine transformations except the QuadtoSOC bridge.
As deletion commutes with affine transformations, except for the QuadtoSOC bridge, there should be no issue just deleting the variable in the bridged solver model.
Deleting a variable part of a quadratic term of a quadratic function bridged by the QuadtoSOC bridged might be an issue though.
We should check that and add tests (with quadratic objective and quadratic constraints).
See https://github.com/jump-dev/MathOptInterface.jl/pull/2150/files#r1173436839
The text was updated successfully, but these errors were encountered: