Skip to content

Commit ffbbc53

Browse files
committed
feat(solver): significantly improve solver performance
- add hints from previous layers to warm start later layers - relative gap limit to avoid slow bound descend - max_time limit to overcome best solution plateau
1 parent 1074e0a commit ffbbc53

1 file changed

Lines changed: 20 additions & 6 deletions

File tree

src/cascade/optimizer/models.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from ortools.sat.python import cp_model
1818

1919
from cascade.compiler import DURATION_UNIT
20-
from cascade.compiler.ast import Status, TaskAST
20+
from cascade.compiler.ast import Status
2121
from .helpers import piecewise_linear_objective
2222
from .piecewise_functions.piecewise_linear_function import (
2323
PiecewiseLinearConstraint,
@@ -144,6 +144,13 @@ def print_schedule(self):
144144
print_formatted_text(formatted_output)
145145

146146

147+
def add_hints(model: cp_model.CpModel, solver: cp_model.CpSolver):
148+
model.clear_hints()
149+
for i, _ in enumerate(model.proto.variables):
150+
v_ = model.get_int_var_from_proto_index(i)
151+
model.add_hint(v_, solver.value(v_))
152+
153+
147154
@dataclass
148155
class BasicModel:
149156
"""
@@ -167,7 +174,11 @@ def get_total_slots(self) -> int:
167174

168175
def solve(self) -> cp_model.CpSolver:
169176
solver = cp_model.CpSolver()
170-
# solver.parameters.log_search_progress = True
177+
solver.parameters.log_search_progress = True
178+
# stop when we are within 2% of the upper bound of optimum (provable) cause making it drop can take a long time.
179+
solver.parameters.relative_gap_limit = 0.02
180+
# since we are adding hints, models are no longer suffering from cold start issues, we might just add a time limit to solve plateau problem
181+
solver.parameters.max_time_in_seconds = 120
171182
status = solver.solve(self.model)
172183

173184
match status:
@@ -306,8 +317,10 @@ def to_cuf_model(self) -> CUFModel:
306317
return CUFModel.from_total_utility_model(self)
307318

308319
def set_objective_constraint(self):
309-
optimal = int(self.solve().objective_value)
320+
sol = self.solve()
321+
optimal = int(sol.objective_value)
310322
self.model.clear_objective()
323+
add_hints(self.model, sol)
311324
self.model.add(sum(utility.y for utility in self.utilities.values()) >= optimal)
312325

313326
@classmethod
@@ -414,9 +427,8 @@ def from_basic_model(cls, basic_model: BasicModel) -> Self:
414427
cuf_prod[id],
415428
[utilities[id].y, total_slots - interval_vars[id].end_expr()],
416429
)
417-
# TODO: This 2x is probably not necessary
418430
cuf[id] = model.new_int_var(
419-
0, 2 * node.priority * total_slots * YSCALE, f"cuf_{id}"
431+
0, node.priority * total_slots * YSCALE, f"cuf_{id}"
420432
)
421433
model.add(cuf[id] == cuf_int[id].y + cuf_prod[id])
422434

@@ -456,8 +468,10 @@ def from_total_utility_model(cls, tu_model: TotalUtilityModel) -> Self:
456468
return cls(**vars(tu_model))
457469

458470
def set_objective_constraint(self):
459-
optimal = int(self.solve().objective_value)
471+
sol = self.solve()
472+
optimal = int(sol.objective_value)
460473
self.model.clear_objective()
474+
add_hints(self.model, sol)
461475
self.model.add(sum(self.cuf.values()) >= optimal)
462476

463477

0 commit comments

Comments
 (0)