Skip to content

Commit 76b28fb

Browse files
authored
make explicit inits for all solvers with standard interface. (#326)
* make explicit inits for all solvers with standard interface. * new changes to apis. Took out warmStart from the core object and into optionsDict. * bump version * fixes #280
1 parent 1915f19 commit 76b28fb

File tree

14 files changed

+322
-173
lines changed

14 files changed

+322
-173
lines changed

HISTORY

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
# PuLP, Copyright J.S. Roy ([email protected]), 2002-2005
22
# Copyright S.A.Mitchell ([email protected]), 2007-
3+
# Copyright F.Peschiera ([email protected]), 2019-
34
# See the LICENSE file for copyright information.
5+
2.3 2020-08-04
6+
Added plugin page in docs
7+
Standardize arguments of solvers to appear in docs
8+
Fixes to import and export of LpProblem and solver
9+
Added warm start to GUROBI
10+
2.2 2020-04-06
11+
Contribution files
12+
Standard arguments: logPath, gapRel, gapAbs.
13+
Import and export solver objects
14+
Import and export LpProblem
15+
Took out amply to its own package
16+
Standard tmp file handling in _CMD solvers
17+
Refactored writeMPS
18+
2.1 2020-04-06
19+
Added MOSEK solver
20+
New documentation
21+
Put tests inside package
22+
Added warm start to CPLEX_PY
23+
2.0 2019-11-23
24+
Restructured solvers code
25+
Added unittests package
26+
Added CHOCO solver
27+
Added warm start for CBC_CMD, GUROBI_CMD, CPLEX_CMD
28+
Automated deploy
429
1.6.1, 2015-12-25
530
Fix for dummy variables
631
1.5.4, 2013-03-18

ROADMAP

Lines changed: 0 additions & 34 deletions
This file was deleted.

pulp/apis/choco_api.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ class CHOCO_CMD(LpSolver_CMD):
3434
"""The CHOCO_CMD solver"""
3535
name = 'CHOCO_CMD'
3636

37+
def __init__(self, path=None, keepFiles=False, mip=True, msg=True, options=None, timeLimit=None):
38+
"""
39+
:param bool mip: if False, assume LP even if integer variables
40+
:param bool msg: if False, no log is shown
41+
:param float timeLimit: maximum time for solver (in seconds)
42+
:param list options: list of additional options to pass to solver
43+
:param bool keepFiles: if True, files are saved in the current directory and not deleted after solving
44+
:param str path: path to the solver binary
45+
"""
46+
LpSolver_CMD.__init__(self, mip=mip, msg=msg, timeLimit=timeLimit,
47+
options=options, path=path, keepFiles=keepFiles)
48+
3749
def defaultPath(self):
3850
return self.executableExtension("choco-parsers-with-dependencies.jar")
3951

@@ -146,11 +158,9 @@ def actualSolve(self, lp, callback=None):
146158
"""Solve a well formulated lp problem"""
147159
raise PulpSolverError("PULP_CHOCO_CMD: Not Available (check permissions on %s)" % self.pulp_choco_path)
148160
else:
149-
def __init__(self, path=None, *args, **kwargs):
150-
"""
151-
just loads up CHOCO_CMD with the path set
152-
"""
161+
def __init__(self, path=None, keepFiles=0, mip=True, msg=True, options=None, timeLimit=None):
153162
if path is not None:
154163
raise PulpSolverError('Use CHOCO_CMD if you want to set a path')
155164
# check that the file is executable
156-
CHOCO_CMD.__init__(self, path=self.pulp_choco_path, *args, **kwargs)
165+
CHOCO_CMD.__init__(self, path=self.pulp_choco_path, keepFiles=keepFiles,
166+
mip=mip, msg=msg, options=options, timeLimit=timeLimit)

pulp/apis/coin_api.py

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,53 @@ class COIN_CMD(LpSolver_CMD):
4242
def defaultPath(self):
4343
return self.executableExtension(cbc_path)
4444

45-
def __init__(self, fracGap = None, maxSeconds = None, *args, **kwargs):
45+
def __init__(self, mip=True, msg=True, timeLimit=None,
46+
fracGap=None, maxSeconds=None, gapRel=None, gapAbs=None,
47+
presolve=None, cuts=None, strong=None, options=None,
48+
warmStart=False, keepFiles=False, path=None, threads=None,
49+
logPath=None, mip_start=False):
4650
"""
47-
:param fracGap:
48-
:param maxSeconds:
49-
:param args:
50-
:param kwargs: includes presolve, cuts, strong
51+
:param bool mip: if False, assume LP even if integer variables
52+
:param bool msg: if False, no log is shown
53+
:param float timeLimit: maximum time for solver (in seconds)
54+
:param float gapRel: relative gap tolerance for the solver to stop (in fraction)
55+
:param float gapAbs: absolute gap tolerance for the solver to stop
56+
:param int threads: sets the maximum number of threads
57+
:param list options: list of additional options to pass to solver
58+
:param bool warmStart: if True, the solver will use the current value of variables as a start
59+
:param bool keepFiles: if True, files are saved in the current directory and not deleted after solving
60+
:param str path: path to the solver binary
61+
:param str logPath: path to the log file
62+
:param bool presolve: if True, adds presolve on
63+
:param bool cuts: if True, adds gomory on knapsack on probing on
64+
:param bool strong: if True, adds strong
65+
:param float fracGap: deprecated for gapRel
66+
:param float maxSeconds: deprecated for timeLimit
67+
:param bool mip_start: deprecated for warmStart
5168
"""
5269

53-
if fracGap:
54-
warnings.warn("Parameter fracGap is being depreciated for standard 'gapRel'")
55-
if 'gapRel' in kwargs:
56-
warnings.warn("Parameter kwargs and fracGap passed, using kwargs")
70+
if fracGap is not None:
71+
warnings.warn("Parameter fracGap is being depreciated for gapRel")
72+
if gapRel is not None:
73+
warnings.warn("Parameter gapRel and fracGap passed, using gapRel")
5774
else:
58-
kwargs['gapRel'] = fracGap
59-
LpSolver_CMD.__init__(self, *args, **kwargs)
60-
if maxSeconds:
61-
warnings.warn("Parameter maxSeconds is being depreciated for standard 'timeLimit'")
62-
if self.timeLimit:
63-
warnings.warn("Parameter timeLimit and maxSeconds passed, using timeLimit ")
75+
gapRel = fracGap
76+
if maxSeconds is not None:
77+
warnings.warn("Parameter maxSeconds is being depreciated for timeLimit")
78+
if timeLimit is not None:
79+
warnings.warn("Parameter timeLimit and maxSeconds passed, using timeLimit")
6480
else:
65-
self.timeLimit = maxSeconds
81+
timeLimit = maxSeconds
82+
if mip_start:
83+
warnings.warn("Parameter mip_start is being depreciated for warmStart")
84+
if warmStart:
85+
warnings.warn("Parameter mipStart and mip_start passed, using warmStart")
86+
else:
87+
warmStart = mip_start
88+
LpSolver_CMD.__init__(self, gapRel=gapRel, mip=mip, msg=msg, timeLimit=timeLimit,
89+
presolve=presolve, cuts=cuts, strong=strong, options=options,
90+
warmStart=warmStart, path=path, keepFiles=keepFiles,
91+
threads=threads, gapAbs=gapAbs, logPath=logPath)
6692

6793
def copy(self):
6894
"""Make a copy of self"""
@@ -96,7 +122,7 @@ def solve_CBC(self, lp, use_mps=True):
96122
constraintsNames = dict((c, c) for c in lp.constraints)
97123
objectiveName = None
98124
cmds = ' ' + tmpLp + " "
99-
if self.warmStart:
125+
if self.optionsDict.get('warmStart', False):
100126
self.writesol(tmpMst, lp, vs, variablesNames, constraintsNames)
101127
cmds += 'mips {} '.format(tmpMst)
102128
if self.timeLimit is not None:
@@ -114,7 +140,8 @@ def solve_CBC(self, lp, use_mps=True):
114140
pipe = None
115141
else:
116142
pipe = open(os.devnull, 'w')
117-
if 'logPath' in self.optionsDict:
143+
logPath = self.optionsDict.get('logPath')
144+
if logPath:
118145
if self.msg:
119146
warnings.warn('`logPath` argument replaces `msg=1`. The output will be redirected to the log file.')
120147
pipe = open(self.optionsDict['logPath'], 'w')
@@ -258,14 +285,19 @@ def actualSolve(self, lp, callback = None):
258285
"""Solve a well formulated lp problem"""
259286
raise PulpSolverError("PULP_CBC_CMD: Not Available (check permissions on %s)" % self.pulp_cbc_path)
260287
else:
261-
def __init__(self, path=None, *args, **kwargs):
262-
"""
263-
just loads up COIN_CMD with the path set
264-
"""
288+
def __init__(self, mip=True, msg=True, timeLimit=None,
289+
fracGap=None, maxSeconds=None, gapRel=None, gapAbs=None,
290+
presolve=None, cuts=None, strong=None, options=None,
291+
warmStart=False, keepFiles=False, path=None, threads=None,
292+
logPath=None, mip_start=False):
265293
if path is not None:
266294
raise PulpSolverError('Use COIN_CMD if you want to set a path')
267295
#check that the file is executable
268-
COIN_CMD.__init__(self, path=self.pulp_cbc_path, *args, **kwargs)
296+
COIN_CMD.__init__(self, path=self.pulp_cbc_path, mip=mip, msg=msg, timeLimit=timeLimit,
297+
fracGap=fracGap, maxSeconds=maxSeconds, gapRel=gapRel, gapAbs=gapAbs,
298+
presolve=presolve, cuts=cuts, strong=strong, options=options,
299+
warmStart=warmStart, keepFiles=keepFiles, threads=threads,
300+
logPath=logPath, mip_start=mip_start)
269301

270302

271303
def COINMP_DLL_load_dll(path):

pulp/apis/core.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -173,32 +173,22 @@ class LpSolver:
173173
"""A generic LP Solver"""
174174
name = 'LpSolver'
175175

176-
def __init__(self, mip = True, msg = True, options = None, mip_start=False, timeLimit=None,
177-
warmStart=False, *args, **kwargs):
176+
def __init__(self, mip=True, msg=True, options=None, timeLimit=None,
177+
*args, **kwargs):
178178
"""
179-
180-
:param bool mip: if False, assume LP even if integer variables.
181-
:param bool msg: if False, no log is shown.
179+
:param bool mip: if False, assume LP even if integer variables
180+
:param bool msg: if False, no log is shown
182181
:param list options:
183-
:param bool warmStart:
184-
:param float timeLimit: maximum time for solver
182+
:param float timeLimit: maximum time for solver (in seconds)
185183
:param args:
186184
:param kwargs: optional named options to pass to each solver,
187185
e.g. gapRel=0.1, gapAbs=10, logPath="",
188-
189186
"""
190187
if options is None:
191188
options = []
192189
self.mip = mip
193190
self.msg = msg
194191
self.options = options
195-
self.warmStart = warmStart
196-
if mip_start:
197-
warnings.warn("Parameter mip_start is being depreciated for standard 'warmStart'")
198-
if warmStart:
199-
warnings.warn("Parameter mipStart and mip_start passed, using warmStart")
200-
else:
201-
self.warmStart = mip_start
202192
self.timeLimit = timeLimit
203193

204194
# here we will store all other relevant information including:
@@ -330,11 +320,17 @@ def getCplexStyleArrays(self,lp, senseDict=None, LpVarCategories=None, LpObjSens
330320

331321
def to_dict(self):
332322
data = dict(solver=self.name)
333-
translate = {}
334-
for v in ['mip', 'msg', 'warmStart', 'timeLimit', 'options', 'keepFiles']:
335-
k = translate.get(v, v)
323+
for k in ['mip', 'msg', 'keepFiles']:
324+
try:
325+
data[k] = getattr(self, k)
326+
except AttributeError:
327+
pass
328+
for k in ['timeLimit', 'options']:
329+
# with these ones, we only export if it has some content:
336330
try:
337-
data[k] = getattr(self, v)
331+
value = getattr(self, k)
332+
if value:
333+
data[k] = value
338334
except AttributeError:
339335
pass
340336
data.update(self.optionsDict)
@@ -350,9 +346,13 @@ class LpSolver_CMD(LpSolver):
350346

351347
name = 'LpSolver_CMD'
352348

353-
def __init__(self, path=None, keepFiles=0, *args, **kwargs):
349+
def __init__(self, path=None, keepFiles=False, *args, **kwargs):
354350
"""
355351
352+
:param bool mip: if False, assume LP even if integer variables
353+
:param bool msg: if False, no log is shown
354+
:param list options: list of additional options to pass to solver (format depends on the solver)
355+
:param float timeLimit: maximum time for solver (in seconds)
356356
:param str path: a path to the solver binary
357357
:param bool keepFiles: if True, files are saved in the current directory and not deleted after solving
358358
:param args: parameters to pass to :py:class:`LpSolver`

0 commit comments

Comments
 (0)