Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
quantresearch1 committed Feb 27, 2025
1 parent 8c2e601 commit 68abd54
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pyomo/contrib/appsi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ def __init__(
)

self.declare('time_limit', ConfigValue(domain=NonNegativeFloat))
self.declare('warmstart', ConfigValue(domain=bool))
self.declare('stream_solver', ConfigValue(domain=bool))
self.declare('load_solution', ConfigValue(domain=bool))
self.declare('symbolic_solver_labels', ConfigValue(domain=bool))
self.declare('report_timing', ConfigValue(domain=bool))

self.time_limit: Optional[float] = None
self.warmstart: bool = False
self.stream_solver: bool = False
self.load_solution: bool = True
self.symbolic_solver_labels: bool = False
Expand Down Expand Up @@ -1525,13 +1527,15 @@ def solve(
options: Optional[Dict] = None,
keepfiles: bool = False,
symbolic_solver_labels: bool = False,
warmstart: bool = False,
):
original_config = self.config
self.config = self.config()
self.config.stream_solver = tee
self.config.load_solution = load_solutions
self.config.symbolic_solver_labels = symbolic_solver_labels
self.config.time_limit = timelimit
self.config.warmstart = warmstart
self.config.report_timing = report_timing
if solver_io is not None:
raise NotImplementedError('Still working on this')
Expand Down
25 changes: 25 additions & 0 deletions pyomo/contrib/appsi/solvers/highs.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,29 @@ def symbol_map(self):
return SymbolMap()
# raise RuntimeError('Highs interface does not have a symbol map')

def warm_start_capable(self):
return True

def _warm_start(self):
# Collect all variable values
col_value = np.zeros(len(self._pyomo_var_to_solver_var_map))
has_values = False

for var_id, col_ndx in self._pyomo_var_to_solver_var_map.items():
var = self._vars[var_id][0]
if var.value is not None:
col_value[col_ndx] = value(var)
has_values = True

if has_values:
solution = highspy.HighsSolution()
solution.col_value = col_value
solution.value_valid = True
solution.dual_valid = False

# Set the solution as a MIP start
self._solver_model.setSolution(solution)

def _solve(self, timer: HierarchicalTimer):
config = self.config
options = self.highs_options
Expand All @@ -246,6 +269,8 @@ def _solve(self, timer: HierarchicalTimer):

for key, option in options.items():
self._solver_model.setOptionValue(key, option)
if config.warmstart:
self._warm_start()
timer.start('optimize')
self._solver_model.run()
timer.stop('optimize')
Expand Down
28 changes: 28 additions & 0 deletions pyomo/contrib/appsi/solvers/tests/test_highs_persistent.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,31 @@ def test_capture_highs_output(self):
self.assertIn("HiGHS run time", OUT.getvalue())
ref = "10.0 5.0\n"
self.assertEqual(ref, OUT.getvalue()[-len(ref) :])

def test_warm_start(self):
m = pe.ConcreteModel()

# decision variables
m.x1 = pe.Var(domain=pe.Integers, name="x1", bounds=(0, 10))
m.x2 = pe.Var(domain=pe.Reals, name="x2", bounds=(0, 10))
m.x3 = pe.Var(domain=pe.Binary, name="x3")

# objective function
m.OBJ = pe.Objective(expr=(3 * m.x1 + 2 * m.x2 + 4 * m.x3), sense=pe.maximize)

# constraints
m.C1 = pe.Constraint(expr=m.x1 + m.x2 <= 9)
m.C2 = pe.Constraint(expr=3 * m.x1 + m.x2 <= 18)
m.C3 = pe.Constraint(expr=m.x1 <= 7)
m.C4 = pe.Constraint(expr=m.x2 <= 6)

# MIP start
m.x1 = 4
m.x2 = 4.5
m.x3 = True

# solving process
with Highs() as opt, capture_output() as output:
opt.solve(m, tee=True, warmstart=True)
log = output.getvalue()
self.assertIn("MIP start solution is feasible, objective value is 25", log)

0 comments on commit 68abd54

Please sign in to comment.