Give a solution to the solver
Hi again!
Most, if not all, of the solver allow to give a feasible solution to the solver. I have a case where I have a solution that is optimal, but I don’t know the optimality. The solver find as a bound the objective value, but continue to search as it doesn’t find the solution. For some solver, it can also improve the resolution.
Can we add this feature? Any remark? How to manage the case of the solver that doesn’t support it (I suspect the executable interface doesn’t always allow this)?a
Yes, that would be nice! It should be a trait implemented by solvers that have this ability. Do you want to open a pull request ?
I might try when I have the time. I’ll ping here if I start.
I'm also interested in this and I'd be willing to work on it. @TeXitoi when would you like to start? I'll have some time in January. Can I steal this from you if you didn't draft a pull request until then? :)
I didn't take the time till now. Feel free to do it, I'll review with pleasure!
@lovasoa I see two ways of implementing this:
- Using a trait that different models can implement so the initial solution can be set via
problem.using(default_solver).initial_solution(init).solve()(you suggested this here) - Using a method on variables so the initial value can be set via
variable().min(0).max(4).initial(2)(you suggested this here)
I personally prefer the second option because it is a lot more ergonomic to use. However, I think that this would prevent the compiler from being able to check whether the used solver actually supports initial values. I believe this to be similar to .integer().
What do you think we should do?
I feel like solution 1 is safer but also more ergonomic. If you have a way to store a solution in a file, you would do something like .initial_solution(load_initial_solution()), instead of having to add code in all the places where you create variables, which can be scattered across multiple functions...
But I guess you are using the library differently, or you had a different use case in mind ?
Ideally, we could have both 1 and 2. Variables could optionally store an initial value, and solvers that implement WithInitialSolution would call WithInitialSolution::set_initial_solution in their initialization function...
We would have to document in Variable::initial that the value will be silently ignored if the solver does not support initial solutions.
But I guess you are using the library differently, or you had a different use case in mind ?
Yes, I have my own data structures and then derive a MILP on the fly. In doing so, I create all the variables and parts of the constraints and continually modify the expressions used in the objective function and the constraints. I have good initial values at this point, so I'd like to add them to newly created variables immediately.
With solution 1, I would have to build up my own vector of initial values alongside the list of variables and then set it afterwards. That is doable, but not as nice as what solution 2 would give me.
Either way, I don't mind adding both.
What should happen if library consumers set initial values for some variables but then also provide a complete initial solution to the solver? Which value should take precedence?
If the initial values stored in the variables are used when the solver is initialized, then the call to .initial_solution(init) will overwrite them. We should document this in the initial_solution docs.
It is not January yet but I could not help it. I started with solution 1. Please check out #71 and roast me.