Solver agnostic way to control warm starting

Is there a way to control/prevent how a LP solver may use information from a previous solve to warm start? I know, e.g., for Gurobi there is LPWarmStart, but I struggle to prevent that for example when using HiGHS.

What happens?
See the MWE at the end. I’d like to use some kind of force_fresh_solve parameter, but could not find anything (even though I vaguely remembered to have seen something similar some time ago…).

Background
Maybe also important why I’m stuck at this: I’m repeatedly solving a model while switching around some settings based on an (un)educated guess of what I expect to be working better for the next solve. Unfortunately, the previous solve prevents a presolve, which hurts more than the warm start helps.

Thanks!


Code for MWE

The following constructs a model, solves it, changes the objective, and re-solves. Not having a previous solution - can be tested by not executing “line 42” - requires 241 simplex iterations, compared to 410 when optimize! was called before.

Note: Happens for dual simplex, or other settings that are changed too.

using JuMP
import HiGHS
import Random: seed!

# Random data.
seed!(12345)
N, M = (500, 500)
A = abs.(randn(N, M))
b = randn(N)
c = abs.(randn(M)), abs.(randn(M))
r = randn(M) .* 100

# Model setup.
m = Model(HiGHS.Optimizer)
set_attribute(m, "simplex_strategy", 4)
@variable(m, r[i] <= x[i = 1:M] <= (r .+ 100)[i])
@constraint(m, A * x .>= b)
@objective(m, Min, c[1]' * x)

# Solve 1.
optimize!(m)      # := line 42

# Modify.
@objective(m, Min, c[2]' * x)

# Solve 2.
optimize!(m)
1 Like

There is no solver-agnostic way to prevent LP warm-starts.

There’s a open upstream issue to add support to HiGHS: Enable forced simplex refactorization · Issue #1598 · ERGO-Code/HiGHS · GitHub

As a high level comment, I get the sense that you are trying too hard to tune the solvers. Worrying about whether it takes 241 or 410 simplex iterations can make a difference for HiGHS and your current test set, but you might find that your parameters etc are suboptimal if you update HiGHS, or if you use some new data in a couple of months. In most cases, letting HiGHS decide to warmstart is probably a good thing.

2 Likes

Having said that, if you are using Model (this won’t work in direct mode), you could try:

model = Model(HiGHS.Optimizer)
# create
optimize!(model)
# modify
MOI.Utilities.reset_optimizer(model)
optimize!(model)

You are totally right with that!


Background

With that said, the MWE was probably chosen too simple (but it was the only one I could quickly draft). One example for a setting that is a “no-brainer” (ok… under specific circumstances), is presolve: off.

It’s really hard convincing HiGHS to turn presolve on again, for example:

  1. solve (ret: infeasible)
  2. presolve: off
  3. resolve => dualray
  4. ensure feasibility
  5. presolve: on
  6. turn on presolve
  7. try resolve.

Having presolve skipped (even though it’s set to on) - based on an infeasible previous run - may (worst case) prevent HiGHS from finding a solution to the now feasible model.

1 Like

That works - nice!

Buuut… yeah, the actual “performance” code makes use of direct_model. I’ll just refactor to using non direct mode for HiGHS for now, and see if that helps. Maybe I don’t need direct mode at all afterwards.

Thanks a lot (as always) :tada:

1 Like

I’m encountering similar issue.
I find that the default setting of Gurobi isn’t satisfying.
The second solve is problematic (e.g. Warning, Markowitz…), after an obj coeff change from the initial solve.
If we solve the second-solve problem from scratch, it is all right.
Therefore I’m also seeking tuning the solver.
Maybe set_attribute(model, "LPWarmStart", 0), I’ll do some tests and then judge.