From a560dc8116b6f70165ff9b369a459eeb46182833 Mon Sep 17 00:00:00 2001 From: Franco Peschiera Date: Tue, 28 Sep 2021 08:58:59 +0200 Subject: [PATCH] added `timeMode` argument for counting cbc wall-time. (#487) * added `timeMode` argument for counting cbc wall-time. * bump pypi version and history * increased the timeLimit and added seed to be sure to pass the test. --- HISTORY | 4 +++ doc/source/develop/contribute.rst | 53 +++++++++++++++++++++++-------- pulp/apis/coin_api.py | 8 +++-- pulp/apis/core.py | 2 +- pulp/constants.py | 2 +- pulp/tests/test_pulp.py | 43 ++++++++++++------------- 6 files changed, 72 insertions(+), 40 deletions(-) diff --git a/HISTORY b/HISTORY index b90f117b..7ce0f0e0 100644 --- a/HISTORY +++ b/HISTORY @@ -7,6 +7,10 @@ fixed bugs added HiGHS solver added pysmps dependency for mps parsing +2.5.1 2021-09-28 + updated docs + fixed minor issues + cbc now uses wall-time for timeLimit 2.5.0 2021-08-11 measuring wall time and cpu time unittests of timeLimit diff --git a/doc/source/develop/contribute.rst b/doc/source/develop/contribute.rst index 0ba15e7f..771f1688 100644 --- a/doc/source/develop/contribute.rst +++ b/doc/source/develop/contribute.rst @@ -1,7 +1,21 @@ How to contribute to PuLP ====================================== -This is a minimalistic guid to setup pulp and help you modify the code and send a PR. +This is a minimalistic guide to setup pulp and help you modify the code and send a PR. + +The quick summary is: + +#. Fork the repo. +#. Clone your forked repo. +#. Install dependencies. +#. Make your changes. +#. Create a test for your changes if needed. +#. Make sure all the tests pass. +#. Lint your code with black. +#. Ensure the docs are accurate. +#. Submit a Pull Request. + + On top of having python installed, we will be using git and the command line. Also, we assume you have a github account and know how to fork a project. We will use plain git through the command line but feel free to use the git client of your choice. @@ -35,19 +49,6 @@ To build pulp from source we wil get inside the pulp directory, then we will cre This will link the pulp version on your virtual environment with the source files in the pulp directory. You can now use pulp from that virtual environment and you will be using the files in the pulp directory. We assume you have run this successfully for all further steps. -Building the documentation ----------------------------- - -The documentation is based in `Sphinx and reStructuredText `_. - -To build the documentation:: - - cd pulp/doc - make html - -A folder named html will be created inside the ``build/`` directory. The home page for the documentation is ``doc/build/html/index.html`` which can be opened in a browser. -You only need to execute ``make html`` to rebuild the docs each time. - Running tests ---------------- @@ -63,6 +64,30 @@ Creating a test When you fix an issue in pulp or add a functionality, you should add a test to the repository. For this you should go to the file `tests/test_pulp.py` and add a new method that tests your change. +Applying the black linter / formatter +----------------------------------------------------- + +We use `the black formatter `_. Before sending your changes, be sure to execute the black package to style the resulting files. +The quickest way to do this is to run: + + python -m black pulp + +And it will do the changes directly on the files. + +The easiest way is to integrate it inside your IDE so it runs every time you save a file. Learn how to do that `in the black integration docs `_. + +Building the documentation +---------------------------- + +The documentation is based in `Sphinx and reStructuredText `_. + +To build the documentation:: + + cd pulp/doc + make html + +A folder named html will be created inside the ``build/`` directory. The home page for the documentation is ``doc/build/html/index.html`` which can be opened in a browser. +You only need to execute ``make html`` to rebuild the docs each time. Making a Pull Request ---------------------------- diff --git a/pulp/apis/coin_api.py b/pulp/apis/coin_api.py index d2ba6c27..da29f908 100644 --- a/pulp/apis/coin_api.py +++ b/pulp/apis/coin_api.py @@ -61,6 +61,7 @@ def __init__( path=None, threads=None, logPath=None, + timeMode="elapsed", mip_start=False, ): """ @@ -80,6 +81,7 @@ def __init__( :param bool strong: if True, adds strong :param float fracGap: deprecated for gapRel :param float maxSeconds: deprecated for timeLimit + :param str timeMode: "elapsed": count wall-time to timeLimit; "cpu": count cpu-time :param bool mip_start: deprecated for warmStart """ @@ -121,6 +123,7 @@ def __init__( threads=threads, gapAbs=gapAbs, logPath=logPath, + timeMode=timeMode, ) def copy(self): @@ -163,8 +166,6 @@ def solve_CBC(self, lp, use_mps=True): self.writesol(tmpMst, lp, vs, variablesNames, constraintsNames) cmds += "mips {} ".format(tmpMst) if self.timeLimit is not None: - if self.optionsDict.get("threads", 1) > 1: - warnings.warn("Beware: CBC uses timeLimit as cpu_time, not wall_time") cmds += "sec %s " % self.timeLimit options = self.options + self.getOptions() for option in options: @@ -226,6 +227,7 @@ def getOptions(self): presolve="presolve on", strong="strong {}", cuts="gomory on knapsack on probing on", + timeMode="timeMode {}", ) return [ @@ -377,6 +379,7 @@ def __init__( threads=None, logPath=None, mip_start=False, + timeMode="elapsed", ): if path is not None: raise PulpSolverError("Use COIN_CMD if you want to set a path") @@ -400,6 +403,7 @@ def __init__( threads=threads, logPath=logPath, mip_start=mip_start, + timeMode=timeMode, ) diff --git a/pulp/apis/core.py b/pulp/apis/core.py index 0fd19a6d..6020adae 100644 --- a/pulp/apis/core.py +++ b/pulp/apis/core.py @@ -231,7 +231,7 @@ def __init__( self.solution_time = 0 # here we will store all other relevant information including: - # gapRel, gapAbs, maxMemory, maxNodes, threads, logPath + # gapRel, gapAbs, maxMemory, maxNodes, threads, logPath, timeMode self.optionsDict = {k: v for k, v in kwargs.items() if v is not None} def available(self): diff --git a/pulp/constants.py b/pulp/constants.py index c84c253b..a3c3db70 100644 --- a/pulp/constants.py +++ b/pulp/constants.py @@ -27,7 +27,7 @@ This file contains the constant definitions for PuLP Note that hopefully these will be changed into something more pythonic """ -VERSION = "2.5.0" +VERSION = "2.5.1" EPS = 1e-7 # variable categories diff --git a/pulp/tests/test_pulp.py b/pulp/tests/test_pulp.py index f9d5ed54..0f3f31b5 100644 --- a/pulp/tests/test_pulp.py +++ b/pulp/tests/test_pulp.py @@ -1148,7 +1148,7 @@ def add_const(prob): def test_measuring_solving_time(self): print("\t Testing measuring optimization time") - time_limit = 5 + time_limit = 10 solver_settings = dict( PULP_CBC_CMD=30, COIN_CMD=30, SCIP_CMD=30, GUROBI_CMD=50, CPLEX_CMD=50 ) @@ -1156,16 +1156,15 @@ def test_measuring_solving_time(self): if bins is None: # not all solvers have timeLimit support return - prob = create_bin_packing_problem(bins=bins) + prob = create_bin_packing_problem(bins=bins, seed=99) self.solver.timeLimit = time_limit prob.solve(self.solver) - delta = 2 + delta = 4 reported_time = prob.solutionTime if self.solver.name in ["PULP_CBC_CMD", "COIN_CMD"]: - # CBC uses cpu-time for timeLimit - # also: CBC is less exact with the timeLimit + # CBC is less exact with the timeLimit reported_time = prob.solutionCpuTime - delta = 4 + delta = 5 self.assertAlmostEqual( reported_time, @@ -1174,6 +1173,22 @@ def test_measuring_solving_time(self): msg="optimization time for solver {}".format(self.solver.name), ) + def test_invalid_var_names(self): + prob = LpProblem(self._testMethodName, const.LpMinimize) + x = LpVariable("a") + w = LpVariable("b") + y = LpVariable("g", -1, 1) + z = LpVariable("End") + prob += x + 4 * y + 9 * z, "obj" + prob += x + y <= 5, "c1" + prob += x + z >= 10, "c2" + prob += -y + z == 7, "c3" + prob += w >= 0, "c4" + print("\t Testing invalid var names") + pulpTestCheck( + prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} + ) + class PULP_CBC_CMDTest(BaseSolverTest.PuLPTest): solveInst = PULP_CBC_CMD @@ -1238,22 +1253,6 @@ class MOSEKTest(BaseSolverTest.PuLPTest): class SCIP_CMDTest(BaseSolverTest.PuLPTest): solveInst = SCIP_CMD - def test_invalid_var_names(self): - prob = LpProblem(self._testMethodName, const.LpMinimize) - x = LpVariable("a") - w = LpVariable("b") - y = LpVariable("g", -1, 1) - z = LpVariable("End") - prob += x + 4 * y + 9 * z, "obj" - prob += x + y <= 5, "c1" - prob += x + z >= 10, "c2" - prob += -y + z == 7, "c3" - prob += w >= 0, "c4" - print("\t Testing invalid var names") - pulpTestCheck( - prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} - ) - def pulpTestCheck( prob,