Skip to content

Commit

Permalink
Add warm start, gap retrieval and better solution statuses for SCIP_P…
Browse files Browse the repository at this point in the history
…Y (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]>
  • Loading branch information
supremeBaboon and agautier authored Jan 12, 2024
1 parent b0856c2 commit 5769e27
Showing 1 changed file with 43 additions and 10 deletions.
53 changes: 43 additions & 10 deletions pulp/apis/scip_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ def __init__(
maxNodes=None,
logPath=None,
threads=None,
warmStart=False,
):
"""
:param bool mip: if False, assume LP even if integer variables
Expand All @@ -504,6 +505,7 @@ def __init__(
:param int maxNodes: max number of nodes during branching. Stops the solving when reached.
:param str logPath: path to the log file
:param int threads: sets the maximum number of threads
:param bool warmStart: if True, the solver will use the current value of variables as a start
"""
super().__init__(
mip=mip,
Expand All @@ -515,6 +517,7 @@ def __init__(
maxNodes=maxNodes,
logPath=logPath,
threads=threads,
warmStart=warmStart,
)

def findSolutionValues(self, lp):
Expand All @@ -538,17 +541,36 @@ def findSolutionValues(self, lp):
"restartlimit": constants.LpStatusNotSolved,
"unknown": constants.LpStatusUndefined,
}
possible_solution_found_statuses = (
"optimal",
"timelimit",
"userinterrupt",
"nodelimit",
"totalnodelimit",
"stallnodelimit",
"gaplimit",
"memlimit",
)
status = scip_to_pulp_status[solutionStatus]
lp.assignStatus(status)

if status == constants.LpStatusOptimal:
solution = lp.solverModel.getBestSol()
for variable in lp._variables:
variable.varValue = solution[variable.solverVar]
for constraint in lp.constraints.values():
constraint.slack = lp.solverModel.getSlack(
constraint.solverConstraint, solution
)

if solutionStatus in possible_solution_found_statuses:
try: # Feasible solution found
solution = lp.solverModel.getBestSol()
for variable in lp._variables:
variable.varValue = solution[variable.solverVar]
for constraint in lp.constraints.values():
constraint.slack = lp.solverModel.getSlack(
constraint.solverConstraint, solution
)
if status == constants.LpStatusOptimal:
lp.assignStatus(status, constants.LpSolutionOptimal)
else:
status = constants.LpStatusOptimal
lp.assignStatus(status, constants.LpSolutionIntegerFeasible)
except: # No solution found
lp.assignStatus(status, constants.LpSolutionNoSolutionFound)
else:
lp.assignStatus(status)

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

##################################################
# add warm start
##################################################
if self.optionsDict.get("warmStart", False):
s = lp.solverModel.createPartialSol()
for var in lp.variables():
if var.varValue is not None:
# Warm start variables having an initial value
lp.solverModel.setSolVal(s, var.solverVar, var.varValue)
lp.solverModel.addSol(s)

def actualSolve(self, lp):
"""
Solve a well formulated lp problem
Expand Down

0 comments on commit 5769e27

Please sign in to comment.