Skip to content

Commit 5769e27

Browse files
supremeBaboonagautier
andauthored
Add warm start, gap retrieval and better solution statuses for SCIP_PY (pyscipopt) (#624)
* Added warm start to pyscipopt * Added a getGap method, to retrieve SCIP's gap. * Changed problem status to Optimal if Solution Found * Changed returned status * Corrected warm-start and removed getGap method * Applied black linter --------- Co-authored-by: agautier <[email protected]>
1 parent b0856c2 commit 5769e27

File tree

1 file changed

+43
-10
lines changed

1 file changed

+43
-10
lines changed

pulp/apis/scip_api.py

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ def __init__(
493493
maxNodes=None,
494494
logPath=None,
495495
threads=None,
496+
warmStart=False,
496497
):
497498
"""
498499
:param bool mip: if False, assume LP even if integer variables
@@ -504,6 +505,7 @@ def __init__(
504505
:param int maxNodes: max number of nodes during branching. Stops the solving when reached.
505506
:param str logPath: path to the log file
506507
:param int threads: sets the maximum number of threads
508+
:param bool warmStart: if True, the solver will use the current value of variables as a start
507509
"""
508510
super().__init__(
509511
mip=mip,
@@ -515,6 +517,7 @@ def __init__(
515517
maxNodes=maxNodes,
516518
logPath=logPath,
517519
threads=threads,
520+
warmStart=warmStart,
518521
)
519522

520523
def findSolutionValues(self, lp):
@@ -538,17 +541,36 @@ def findSolutionValues(self, lp):
538541
"restartlimit": constants.LpStatusNotSolved,
539542
"unknown": constants.LpStatusUndefined,
540543
}
544+
possible_solution_found_statuses = (
545+
"optimal",
546+
"timelimit",
547+
"userinterrupt",
548+
"nodelimit",
549+
"totalnodelimit",
550+
"stallnodelimit",
551+
"gaplimit",
552+
"memlimit",
553+
)
541554
status = scip_to_pulp_status[solutionStatus]
542-
lp.assignStatus(status)
543-
544-
if status == constants.LpStatusOptimal:
545-
solution = lp.solverModel.getBestSol()
546-
for variable in lp._variables:
547-
variable.varValue = solution[variable.solverVar]
548-
for constraint in lp.constraints.values():
549-
constraint.slack = lp.solverModel.getSlack(
550-
constraint.solverConstraint, solution
551-
)
555+
556+
if solutionStatus in possible_solution_found_statuses:
557+
try: # Feasible solution found
558+
solution = lp.solverModel.getBestSol()
559+
for variable in lp._variables:
560+
variable.varValue = solution[variable.solverVar]
561+
for constraint in lp.constraints.values():
562+
constraint.slack = lp.solverModel.getSlack(
563+
constraint.solverConstraint, solution
564+
)
565+
if status == constants.LpStatusOptimal:
566+
lp.assignStatus(status, constants.LpSolutionOptimal)
567+
else:
568+
status = constants.LpStatusOptimal
569+
lp.assignStatus(status, constants.LpSolutionIntegerFeasible)
570+
except: # No solution found
571+
lp.assignStatus(status, constants.LpSolutionNoSolutionFound)
572+
else:
573+
lp.assignStatus(status)
552574

553575
# TODO: check if problem is an LP i.e. does not have integer variables
554576
# if :
@@ -649,6 +671,17 @@ def buildSolverModel(self, lp):
649671
name=name,
650672
)
651673

674+
##################################################
675+
# add warm start
676+
##################################################
677+
if self.optionsDict.get("warmStart", False):
678+
s = lp.solverModel.createPartialSol()
679+
for var in lp.variables():
680+
if var.varValue is not None:
681+
# Warm start variables having an initial value
682+
lp.solverModel.setSolVal(s, var.solverVar, var.varValue)
683+
lp.solverModel.addSol(s)
684+
652685
def actualSolve(self, lp):
653686
"""
654687
Solve a well formulated lp problem

0 commit comments

Comments
 (0)