Conversation
Codecov Report
@@ Coverage Diff @@
## master #387 +/- ##
==========================================
+ Coverage 96.35% 96.37% +0.02%
==========================================
Files 39 39
Lines 4913 4913
==========================================
+ Hits 4734 4735 +1
+ Misses 179 178 -1
Continue to review full report at Codecov.
|
| """ | ||
| emptycopy(model::ModelLike) | ||
|
|
||
| Return an independent empty copy of `model`. This can be used to duplicate a |
There was a problem hiding this comment.
What is exactly is copied over, if anything? Solver parameters?
There was a problem hiding this comment.
No, nothing at all. The solver parameters will be copied if it is followed by copy! (as we will do in JuMP).
There was a problem hiding this comment.
What about the license environment?
There was a problem hiding this comment.
What is the issue with license environment ?
Maybe the easiest is to say that JuMP uses this by doing emptycopy followed by copy! so everything that will not be carried over by copy! need to be copied by emptycopy.
There was a problem hiding this comment.
Gurobi has an Env that needs to be copied when you create a new model. The Env has both license information and solver parameters.
There was a problem hiding this comment.
emptycopy is not needed anymore thanks to JuMP factories
There was a problem hiding this comment.
I think this is a useful function to have, but we're blocked until we define a clear concept for what is copied over to the empty model and how licences should be handled.
There was a problem hiding this comment.
If this is still useful, should I open an issue? Or can this PR be closed without one.
There was a problem hiding this comment.
Not sure, we could just close, there is no need for it even if it could be useful
|
I vote for reviving this proposal and removing |
|
An argument for factories is that when you do model = Model(Solver.Optimizer())and internally it does function Model(opt::Optimizer)
new_opt = MOI.empty_copy(opt)
return Model(..., new_opt, ...)
endIt seems less clean because the user has to create an optimizer that is not even used. The counter-argument is that an empty optimizer object is light-weight so it does not matter. |
|
Why not define MOI solver factories? |
|
Yes, we could do that also. The underlying issue is that most modeling interfaces would typically use strings or enums to select solvers because they have a finite number of solvers available. We don't have that luxury. |
|
We can make the problem a bit simpler by normalizing the constructors for optimizers. For example, suppose we say that every optimizer needs to have a constructor like: function MyOptimizer(solver_parameters::Array{Pair{String, Any}})
...
endThen we can instantiate an optimizer given its type and its list of parameters (which is plain data). No need for a closure. No In JuMP this could look like: model = Model(optimizer=Ipopt.Optimizer, solve_parameters=["print_level" => 10])
...
set_optimizer(Gurobi.Optimizer, solve_parameters=["env" => my_env, "NodeLimit" => 10])
...
set_optimizer(Pajarito.Optimizer, solve_parameters=["mip_optimizer" => Gurobi.Optimizer, "mip_parameters" => ["NodeLimit" => 10]])Note I chose to use strings instead of symbols because parameter names aren't identifiers (http://www.juliaopt.org/JuMP.jl/v0.19.0/style/#@enum-vs.-Symbol-1). Ipopt has parameter names with periods in them, for example. Another option is to avoid taking parameters in the constructor when they're not needed at construction time, e.g., set_optimizer(model, Ipopt.Optimizer)
set_solve_parameter(model, "print_level", 10)Some values like license contexts are needed at construction time, but most parameters are not. You then wouldn't have long awkward lists passed to |
That would work
Setting parameters after model creation is not an option if you give a factory to a function which creates a model internally. You can solve it with closure by creating a closure that creates the optimizer, set parameters and return it but this is painful |
I'm proposing this: function instantiate_solver(optimizer_type, constructor_kwargs, solve_params)
optimizer = optimizer_type(;constructor_kwargs...)
for (k, v) in solve_params
set(optimizer, RawParameter(k), v)
end
return optimizer
endYou pass |
Defining MOI solver factories fixes this: Juniper.Optimizer(mip_solver = MOI.with_optimizer(Gurobi.Optimizer, OutputFlag=0)) |
|
I don't support moving OptimizerFactory as-is to MOI. It's a confusing data structure to work with. Look at the definition: I'm more convinced now that we should stop using constructor keyword arguments as the main way to pass solver-specific parameters. Using something like |
|
I like the idea of simplifying the optimizer constructor. For most solvers, that means that the constructors will now be empty as they don't need any parameter at construction time. However, asking for with_optimizer(MOIU.MockOptimizer, JuMP._MOIModel{Float64}())Note that if we do |
Exactly.
I'd argue to just delete the The motivation for |
|
Moving it to v0.10. If someone is willing to do this in the next days, we can move it back to v0.9. |
|
Can this be closed since we now have |
|
SGTM |
This is needed to implement
Base.copyin JuMP. (see jump-dev/JuMP.jl#1300).