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

Attributes simultaneously set by the user and by the optimizer #2345

Closed
pedromxavier opened this issue Nov 6, 2023 · 7 comments · Fixed by #2350
Closed

Attributes simultaneously set by the user and by the optimizer #2345

pedromxavier opened this issue Nov 6, 2023 · 7 comments · Fixed by #2350
Labels
Type: Documentation This issue requires changes to the documentation
Milestone

Comments

@pedromxavier
Copy link
Contributor

I am having some trouble with attributes that are set both by the user and by the optimizer. I think that the UI I am trying to provide collides with the assumptions made by the supports/is_set_by_optimize/is_copyable interface.

To be more clear, in JuliaQUBO/ToQUBO.jl#77 we are having a recurrent instability with the ToQUBO.Attributes.ConstraintEncodingPenalty <: ... <: MOI.AbstractConstraintAttribute attribute.
This attribute is meant to allow the user to set manually the penalty factor that will multiply the encoding of a variable in a unconstrained setting.
If not provided, our meta-solver will compute it automatically.
There is even a third scenario where the solver finds even tighter bounds and might select computed values regardless.

In the last two cases, the user will likely want to query the computed results after the call to the optimizer.
In our latest CI run, there are two broken tests where the MOI.get(model, ToQUBO.Attributes.ConstraintEncodingPenalty(), c) calls reach a key not found-like error in an interaction with the DoubleDict struct that maps constraints over bridges.
This error appears after setting is_set_by_optimizer and is_copyable to true. is_copyable is set to avoid another error related to support. is_set_by_optimizer, on the other hand, is necessary for allowing the penalty values to be requested by the caching optimizer on top of it.

Is there any way to stick to the interface while still providing the desired interface?

I thought of copying these attributes after optimize! is called. This would work if ToQUBO.Optimizer is accessed directly by a MOI/JuMP model but would fail if MOI.copy_to or MOI.optimize!(dst, src) were called by other solvers.

@odow
Copy link
Member

odow commented Nov 6, 2023

Hmm. I think I need to think about this. We haven't considered an attribute that is both set by the optimizer, and set by the user.

The closest is probably VariablePrimalStart and VariablePrimal. We made them separate attributes, rather than the user being able to set VariablePrimal.

@blegat
Copy link
Member

blegat commented Nov 7, 2023

calls reach a key not found-like error in an interaction with the DoubleDict struct that maps constraints over bridges.

Could you be more specific on the error you are getting ?

@pedromxavier
Copy link
Contributor Author

@joaquimg gave a similar idea.
Maybe the way to go is to create ToQUBO.Attributes.ConstraintEncodingPenaltyHint for MOI.get and let ToQUBO.Attributes.ConstraintEncodingPenalty for querying after MOI.optimize!.
What do you think @bernalde @pedroripper?

The error stack is:

Testing Running tests...
Attributes: Error During Test at /mnt/d/gits/ToQUBO.jl/test/integration/interface.jl:254
  Test threw exception
  Expression: MOI.get(model, Attributes.ConstraintEncodingPenalty(), c[1]) === nothing
  KeyError: key MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}(1) not found
  Stacktrace:
    [1] (::MathOptInterface.Utilities.DoubleDicts.var"#1#2"{MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}})()
      @ MathOptInterface.Utilities.DoubleDicts ~/.julia/packages/MathOptInterface/S3qs3/src/Utilities/DoubleDicts.jl:191
    [2] get
      @ ./dict.jl:533 [inlined]
    [3] getindex
      @ ~/.julia/packages/MathOptInterface/S3qs3/src/Utilities/DoubleDicts.jl:190 [inlined]
    [4] getindex
      @ ~/.julia/packages/MathOptInterface/S3qs3/src/Utilities/DoubleDicts.jl:183 [inlined]
    [5] getindex
      @ ~/.julia/packages/MathOptInterface/S3qs3/src/Utilities/copy/index_map.jl:67 [inlined]
    [6] get(model::MathOptInterface.Utilities.CachingOptimizer{ToQUBO.Virtual.Model{Float64}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, attr::ToQUBO.Attributes.ConstraintEncodingPenalty, index::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}})
      @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/S3qs3/src/Utilities/cachingoptimizer.jl:911
    [7] get
      @ ~/.julia/packages/MathOptInterface/S3qs3/src/Bridges/bridge_optimizer.jl:1456 [inlined]
    [8] macro expansion
      @ ~/.julia/juliaup/julia-1.9.3+0.x64.linux.gnu/share/julia/stdlib/v1.9/Test/src/Test.jl:478 [inlined]
    [9] macro expansion
      @ /mnt/d/gits/ToQUBO.jl/test/integration/interface.jl:254 [inlined]
   [10] macro expansion
      @ ~/.julia/juliaup/julia-1.9.3+0.x64.linux.gnu/share/julia/stdlib/v1.9/Test/src/Test.jl:1498 [inlined]
   [11] macro expansion
      @ /mnt/d/gits/ToQUBO.jl/test/integration/interface.jl:88 [inlined]
   [12] macro expansion
      @ ~/.julia/juliaup/julia-1.9.3+0.x64.linux.gnu/share/julia/stdlib/v1.9/Test/src/Test.jl:1498 [inlined]
   [13] test_interface_moi()
      @ Main /mnt/d/gits/ToQUBO.jl/test/integration/interface.jl:21

@odow
Copy link
Member

odow commented Nov 8, 2023

Maybe the way to go is to create ToQUBO.Attributes.ConstraintEncodingPenaltyHint for MOI.get and let ToQUBO.Attributes.ConstraintEncodingPenalty for querying after MOI.optimize!

Yeah. I think so. With the assumption in your solver that the Hint is used exactly. (Perhaps the solver is free to chose a more expensive penalty?)

@odow
Copy link
Member

odow commented Nov 8, 2023

We've certainly tried to separate "what are the attributes required as input that the user provides" and "what is the data from the solver that is produced as output."

Your initial ToQUBO.Attributes.ConstraintEncodingPenalty is a blend of these two things.

For example, after the solve, how would one tell which values were provided by the user and which were computed by the solver?

@pedromxavier
Copy link
Contributor Author

Those are valuable comments, @odow, thank you.

Indeed, if the computed value was to be the same as the user-provided hint we would not be able to tell them apart.

On our side, the choice for an attribute pair seems to be the way to go and this issue could be closed.
On the other hand, it would be nice to have a more clear explanation about how is_set_by_optimizer and is_copyable work. The VariablePrimalStart example seems to illustrate it well.

@odow odow added the Type: Documentation This issue requires changes to the documentation label Nov 8, 2023
@odow odow added this to the v1.x milestone Nov 8, 2023
@blegat
Copy link
Member

blegat commented Nov 8, 2023

Yes, and the error message should be improved into something like : "This is a attribute set by optimize! and you haven't called optimize! yet"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Documentation This issue requires changes to the documentation
Development

Successfully merging a pull request may close this issue.

3 participants