You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I wish to use initial solutions to the MIP and LP HiGHS solver using the APPSI HighsPy interface.
Rationale
It is not uncommon to be able to create decent initial solutions to a MIP problem, for example, by using a heuristic. The MIP solver can then use the initial solution to speed up the solving process - potentially giving you an optimal or better solution, if you're time constrained in solving the MIP model.
Being able to pass solutions to a MIP solver also allows for better naive implementations of various LP/MIP solving techniques, such as a Bender's Decomposition or a Rolling Horizon Heuristic.
This can also be helpful for LP problems, I'm not sure if a similar thing can be done for the QP solver.
Description
The current solve functions found in APPSI Highspy interface look like this:
I expect there to be a call to self._solver_model.setSolution filled with the values I've passed to my Pyomo model in order to pass an initial solution for an LP/MIP problem. However, I was not able to find this call anywhere in the methods I've listed above, nor in any of the methods called inside solve and _solve.
Why I think solver_model.setSolution is the correct function to use here:
I am also open to other suggestions on how to pass initial solutions to HiGHs. For example, the CLI also supports passing a solution file: https://ergo-code.github.io/HiGHS/dev/executable/ - So an alternative could be to build an old-school CLI-based Pyomo solver, that passes the initial solution using the --read_solution_file option. But I think this is a rather crappy alternative, because this sort of implementation does not provide the benefits APPSI does (i.e. a persistent solver).
Additional information
I'm interested in this topic because I want to implement a Rolling Horizon Heursitic using HiGHS and Pyomo. My code looks like this:
defrolling_horizon_solve(model: pyo.Model, solver: Highs, config: OptConfig):
original_time_limit=solver.config.time_limitsolver.config.time_limit=config.MILP_ROLLING_HORIZON_TIME_LIMITall_dates=sorted(model.dates)
logger.info(f"Starting rolling horizon solve for {len(all_dates)} dates.")
fori, rolling_horizon_dateinenumerate(all_dates):
logger.info(f"Rolling horizon solve for date {rolling_horizon_date}.")
__fix_variables_for_date(model, all_dates, rolling_horizon_date)
solver.solve(model)
__unfix_variables(model)
# TODO: Error handling if no solution found!# (Escape from loop to master problem solve)solver.load_vars()
solver.config.time_limit=original_time_limitlogger.info("Completed rolling horizon solve. Starting master problem solve.")
# Master problem solvereturnsolver.solve(model)
Where __fix_variables_for_date fixes past variables to their current values, and future ones to zero. Essentially, many calls to model.example_var_set.fix(0.0) (for future variables) and my_var.fix(my_var.value) (for past variables).
In any case, this heuristic is able to find a decent initial solution rather quickly (<10 minutes). What I want to do is to feed this initial solution back into my master problem, which runs for about 6 hours in total. The solution I find using this heuristic is about as good as the solution by cold starting my master problem and letting it compute for 2 hours. So if I'm able to pass the initial solution from the heuristic into my master problem, I'd get a little under 2 hours of more headroom to find a better solution to my master problem.
The thing is: The master problem solve does not benefit from the rolling horizon heuristic I did above it in my python snippet. Indeed, the solution logs for my master problem solve, with the rolling horizon heuristc activated, look like this for a simplified dev-model I ran for 200 seconds:
Whereas the solution logs for the last iteration of the rolling horizon heuristic look like this:
Coefficient ranges:
Matrix [8e-03, 1e+02]
Cost [7e-06, 1e+02]
Bound [9e-02, 1e+04]
RHS [3e-02, 1e+03]
Presolving model
30994 rows, 35919 cols, 77100 nonzeros 0s
20416 rows, 26216 cols, 64779 nonzeros 0s
17305 rows, 20801 cols, 55271 nonzeros 0s
12128 rows, 19736 cols, 43220 nonzeros 0s
10736 rows, 17155 cols, 37234 nonzeros 0s
Solving MIP model with:
10736 rows
17155 cols (51 binary, 5588 integer, 1255 implied int., 10261 continuous)
37234 nonzeros
Src: B => Branching; C => Central rounding; F => Feasibility pump; H => Heuristic; L => Sub-MIP;
P => Empty MIP; R => Randomized rounding; S => Solve LP; T => Evaluate node; U => Unbounded;
z => Trivial zero; l => Trivial lower; u => Trivial upper; p => Trivial point
Nodes | B&B Tree | Objective Bounds | Dynamic Constraints | Work
Src Proc. InQueue | Leaves Expl. | BestBound BestSol Gap | Cuts InLp Confl. | LpIters Time
0 0 0 0.00% 12201.033907 inf inf 0 0 0 0 0.2s
0 0 0 0.00% 40658.399534 inf inf 0 0 3 4281 0.4s
C 0 0 0 0.00% 47334.451323 84815.714809 44.19% 5755 1105 172 8601 1.8s
L 0 0 0 0.00% 49247.175821 50209.5156 1.92% 8960 1255 172 10864 7.3s
0.9% inactive integer columns, restarting
Model after restart has 10639 rows, 17001 cols (48 bin., 5542 int., 1251 impl., 10160 cont.), and 36922 nonzeros
0 0 0 0.00% 49299.615591 50209.5156 1.81% 975 0 0 21174 8.7s
0 0 0 0.00% 49300.365256 50209.5156 1.81% 975 919 3 26000 8.9s
L 0 0 0 0.00% 49702.558372 49876.730482 0.35% 2993 1091 3 27104 15.0s
1 0 1 100.00% 49702.558372 49876.730482 0.35% 2993 1091 3 33356 15.1s
Solving report
Status Optimal
Primal bound 49876.7304815
Dual bound 49702.5583724
Gap 0.349% (tolerance: 1%)
P-D integral 2.3908187846
Solution status feasible
49876.7304815 (objective)
0 (bound viol.)
2.50022225146e-13 (int. viol.)
0 (row viol.)
Timing 15.07 (total)
0.00 (presolve)
0.00 (solve)
0.00 (postsolve)
Max sub-MIP depth 7
Nodes 1
Repair LPs 0 (0 feasible; 0 iterations)
LP iterations 33356 (total)
0 (strong br.)
7776 (separation)
16473 (heuristics)
What I expect to happen is that the master problem solve starts off with the initial solution provided by the last iteration of the rolling horizon heuristic, and thus start with a primal bound of 49876.730482 - the dual bound should get reset and recomputed, since the underlying MIP has less constraints.
The text was updated successfully, but these errors were encountered:
I think I've done enough research here that I might be able to do implement this feature on my own into the repository if nobody else wants to grab this issue.
What would be nice to know beforehand is if I misunderstood the documentation and if there's actually a way to pass initial solutions to the HiGHS-APPSI interface, so I don't stress myself out doing some unnecessary changes :) - I'd also be very thankful if someone else does the changes.
Summary
I wish to use initial solutions to the MIP and LP HiGHS solver using the APPSI HighsPy interface.
Rationale
It is not uncommon to be able to create decent initial solutions to a MIP problem, for example, by using a heuristic. The MIP solver can then use the initial solution to speed up the solving process - potentially giving you an optimal or better solution, if you're time constrained in solving the MIP model.
Being able to pass solutions to a MIP solver also allows for better naive implementations of various LP/MIP solving techniques, such as a Bender's Decomposition or a Rolling Horizon Heuristic.
This can also be helpful for LP problems, I'm not sure if a similar thing can be done for the QP solver.
Description
The current solve functions found in APPSI Highspy interface look like this:
I expect there to be a call to
self._solver_model.setSolution
filled with the values I've passed to my Pyomo model in order to pass an initial solution for an LP/MIP problem. However, I was not able to find this call anywhere in the methods I've listed above, nor in any of the methods called insidesolve
and_solve
.Why I think solver_model.setSolution is the correct function to use here:
I am also open to other suggestions on how to pass initial solutions to HiGHs. For example, the CLI also supports passing a solution file: https://ergo-code.github.io/HiGHS/dev/executable/ - So an alternative could be to build an old-school CLI-based Pyomo solver, that passes the initial solution using the
--read_solution_file
option. But I think this is a rather crappy alternative, because this sort of implementation does not provide the benefits APPSI does (i.e. a persistent solver).Additional information
I'm interested in this topic because I want to implement a Rolling Horizon Heursitic using HiGHS and Pyomo. My code looks like this:
Where
__fix_variables_for_date
fixes past variables to their current values, and future ones to zero. Essentially, many calls tomodel.example_var_set.fix(0.0)
(for future variables) andmy_var.fix(my_var.value)
(for past variables).In any case, this heuristic is able to find a decent initial solution rather quickly (<10 minutes). What I want to do is to feed this initial solution back into my master problem, which runs for about 6 hours in total. The solution I find using this heuristic is about as good as the solution by cold starting my master problem and letting it compute for 2 hours. So if I'm able to pass the initial solution from the heuristic into my master problem, I'd get a little under 2 hours of more headroom to find a better solution to my master problem.
The thing is: The master problem solve does not benefit from the rolling horizon heuristic I did above it in my python snippet. Indeed, the solution logs for my master problem solve, with the rolling horizon heuristc activated, look like this for a simplified dev-model I ran for 200 seconds:
Whereas the solution logs for the last iteration of the rolling horizon heuristic look like this:
What I expect to happen is that the master problem solve starts off with the initial solution provided by the last iteration of the rolling horizon heuristic, and thus start with a primal bound of 49876.730482 - the dual bound should get reset and recomputed, since the underlying MIP has less constraints.
The text was updated successfully, but these errors were encountered: