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

Custom attributes not queried correctly with caching optimizer #2587

Closed
odow opened this issue May 21, 2020 · 11 comments
Closed

Custom attributes not queried correctly with caching optimizer #2587

odow opened this issue May 21, 2020 · 11 comments
Labels
Status: Needs developer call This should be discussed on a monthly developer call
Milestone

Comments

@odow
Copy link
Member

odow commented May 21, 2020

See https://stackoverflow.com/questions/61922425/how-can-i-determine-whether-a-jump-model-solved-by-gurobi-is-a-mip

Issue is in functions like this:
https://github.com/JuliaOpt/MathOptInterface.jl/blob/3f872f9edb50b1a97cb65011430a0160fd56111e/src/Utilities/cachingoptimizer.jl#L539-L557
where we need a branch to see if it is possible to query directly from the optimizer.

@blegat
Copy link
Member

blegat commented May 21, 2020

One option would be to make ModelAttribute("IsMIP") be an optimizer attribute. The reasoning of the caching optimizer code is: If it's a model attribute then as the cache has the full model, it should be able to answer to get. Here the cache would be able to answer to IsMIP, it can just check whether there are integer/binary variables.
So another option would be to make this a solver-independent attribute. In general, I think solvers should not define custom model attributes that should be get but never set. If it's a custom attribute that can be set and then get (like callbacks) then it's ok for it to be model attributes as the cache will also have the attribute set so it can answer to get.

@odow
Copy link
Member Author

odow commented May 21, 2020

One option would be to make ModelAttribute("IsMIP") be an optimizer attribute.

But there are also variable and constraint attributes.

In general, I think solvers should not define custom model attributes that should be get but never set.

I don't think this is a workable solution. We have a counter-example with Gurobi, and there are bound to be other examples.

@blegat
Copy link
Member

blegat commented May 22, 2020

We could have a function similar to MOI.is_set_by_optimize like MOI.is_set_by_optimizer. Here it seems the attribute is set by the optimizer even when TerminationStatus is OPTIMIZE_NOT_CALLED so returning true for is_set_by_optimize would not work, correct ?

@odow
Copy link
Member Author

odow commented May 22, 2020

is_set_by_optimizer seems sensible. Or something like is_optimizer_specific.

@odow
Copy link
Member Author

odow commented May 22, 2020

@odow
Copy link
Member Author

odow commented May 22, 2020

Actually, we probably want is_attribute_from_optimizer(attr) so that users don't have to do this.

@blegat
Copy link
Member

blegat commented May 24, 2020

Actually, we probably want is_attribute_from_optimizer(attr) so that users don't have to do this.

Sounds good

@odow
Copy link
Member Author

odow commented May 7, 2021

I think this could be better handled via JuMP:

function JuMP.get_optimizer_attribute(
    model::Model,
    attr::MOI.AbstractModelAttribute,
)
    return MOI.get(backend(model), MOI.Utilities.AttributeFromOptimizer(attr))
end

model = Model(Gurobi.Optimizer)
MOI.Utilities.attach_optimizer(model)
get_optimizer_attribute(model, Gurobi.ModelAttribute("IsMIP"))

At the MOI level, it's sufficient to wrap attributes in AttributeFromOptimizer.

@odow odow transferred this issue from jump-dev/MathOptInterface.jl May 7, 2021
@odow
Copy link
Member Author

odow commented May 7, 2021

This works. Needs docs and tests

function JuMP.get_optimizer_attribute(
    model::Model, 
    attr::MOI.AbstractModelAttribute,
)
    b = backend(model)
    if mode(model) == DIRECT
        return MOI.get(b, attr)
    end
    return MOI.get(b, MOI.Utilities.AttributeFromOptimizer(attr))
end

function JuMP.get_optimizer_attribute(
    model::Model,
    attr::MOI.AbstractConstraintAttribute,
    c::ConstraintRef,
)
    b = backend(model)
    if mode(model) == DIRECT
        return MOI.get(b, attr, index(c))
    else
        return MOI.get(b, MOI.Utilities.AttributeFromOptimizer(attr), index(c))
    end
end

function JuMP.get_optimizer_attribute(
    model::Model,
    attr::MOI.AbstractVariableAttribute,
    x::VariableRef,
)
    b = backend(model)
    if mode(model) == DIRECT
        return MOI.get(b, attr, index(x))
    else
        return MOI.get(b, MOI.Utilities.AttributeFromOptimizer(attr), index(x))
    end
end

model = Model(Gurobi.Optimizer)
@variable(model, x >= 1.5)
@constraint(model, c, 2x + 1 == 0)
MOI.Utilities.attach_optimizer(model)
get_optimizer_attribute(model, Gurobi.ModelAttribute("IsMIP")) == 0
get_optimizer_attribute(model, Gurobi.VariableAttribute("LB"), x) == 1.5
get_optimizer_attribute(model, Gurobi.ConstraintAttribute("RHS"), c) == -1.0

@odow odow added good first issue This issue is recommended for new users, it does not require a thorough understanding of the package Status: Help Wanted Help is welcome on this issue labels May 7, 2021
@joaquimg
Copy link
Member

joaquimg commented May 7, 2021

How much of that extends to other solvers?
Or is that Gurobi's interface?

Also, what about cases where you want to query attributes from a pair of variables (in a quadratic) and a pair variable-constraint (coefficients for instance).

I had a use case in DiffOpt.jl where we want to query derivatives from all problem data.

Maybe we can have a more generic method with some "args..." and pass them through a convert_to_index_if_needed function?

@odow odow added this to the 1.0 milestone Sep 23, 2021
@odow odow added Status: Needs developer call This should be discussed on a monthly developer call and removed Status: Help Wanted Help is welcome on this issue good first issue This issue is recommended for new users, it does not require a thorough understanding of the package labels Oct 25, 2021
@odow
Copy link
Member Author

odow commented Nov 1, 2021

Closing in favor of jump-dev/Gurobi.jl#425

@odow odow closed this as completed Nov 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Needs developer call This should be discussed on a monthly developer call
3 participants