diff --git a/doc/source/develop/add_solver.rst b/doc/source/develop/add_solver.rst index 6beb4b9d..333527d1 100644 --- a/doc/source/develop/add_solver.rst +++ b/doc/source/develop/add_solver.rst @@ -92,13 +92,13 @@ Takes an :py:class:`pulp.pulp.LpProblem` as argument, solves it, stores the solu os.remove(tmpSol) except: pass - cmd = self.path - cmd += " %s" % tmpMps - cmd += " -solfile %s" % tmpSol + cmd = [self.path] + cmd.append(tmpMps) + cmd.extend(["-solfile", tmpSol]) if self.timeLimit is not None: - cmd += " -time %s" % self.timeLimit + cmd.extend(["-time", f"{self.timeLimit}"]) for option in self.options: - cmd += " " + option + cmd.append(option) if lp.isMIP(): if not self.mip: warnings.warn("MIPCL_CMD cannot solve the relaxation of a problem") @@ -107,7 +107,7 @@ Takes an :py:class:`pulp.pulp.LpProblem` as argument, solves it, stores the solu else: pipe = open(os.devnull, "w") - return_code = subprocess.call(cmd.split(), stdout=pipe, stderr=pipe) + return_code = subprocess.call(cmd, stdout=pipe, stderr=pipe) # We need to undo the objective swap before finishing if lp.sense == constants.LpMaximize: lp += -lp.objective diff --git a/pulp/apis/choco_api.py b/pulp/apis/choco_api.py index 5d37a026..ddb502e2 100644 --- a/pulp/apis/choco_api.py +++ b/pulp/apis/choco_api.py @@ -89,13 +89,15 @@ def actualSolve(self, lp): os.remove(tmpSol) except: pass - cmd = java_path + ' -cp "' + self.path + '" org.chocosolver.parser.mps.ChocoMPS' + cmd = [java_path, "-cp", self.path, "org.chocosolver.parser.mps.ChocoMPS"] if self.timeLimit is not None: - cmd += f" -limit [-{self.timeLimit}s]" - cmd += " " + " ".join([f"{key} {value}" for key, value in self.options]) - cmd += f" {tmpMps}" + cmd.extend(["-limit", f"[-{self.timeLimit}s]"]) + cmd.extend( + [key_or_value for key_value in self.options for key_or_value in key_value] + ) + cmd.append(tmpMps) if lp.sense == constants.LpMaximize: - cmd += " -max" + cmd.append("-max") if lp.isMIP(): if not self.mip: warnings.warn("CHOCO_CMD cannot solve the relaxation of a problem") diff --git a/pulp/apis/gurobi_api.py b/pulp/apis/gurobi_api.py index d9e597ec..6553d75c 100644 --- a/pulp/apis/gurobi_api.py +++ b/pulp/apis/gurobi_api.py @@ -459,23 +459,23 @@ def actualSolve(self, lp): os.remove(tmpSol) except: pass - cmd = self.path + cmd = [self.path] options = self.options + self.getOptions() if self.timeLimit is not None: options.append(("TimeLimit", self.timeLimit)) - cmd += " " + " ".join([f"{key}={value}" for key, value in options]) - cmd += f" ResultFile={tmpSol}" + cmd.extend(f"{key}={value}" for key, value in options) + cmd.append(f"ResultFile={tmpSol}") if self.optionsDict.get("warmStart", False): self.writesol(filename=tmpMst, vs=vs) - cmd += f" InputFile={tmpMst}" + cmd.append(f"InputFile={tmpMst}") if lp.isMIP(): if not self.mip: warnings.warn("GUROBI_CMD does not allow a problem to be relaxed") - cmd += f" {tmpLp}" + cmd.append(tmpLp) pipe = self.get_pipe() - return_code = subprocess.call(cmd.split(), stdout=pipe, stderr=pipe) + return_code = subprocess.call(cmd, stdout=pipe, stderr=pipe) # Close the pipe now if we used it. if pipe is not None: diff --git a/pulp/apis/mipcl_api.py b/pulp/apis/mipcl_api.py index d1108062..7fe9d5ff 100644 --- a/pulp/apis/mipcl_api.py +++ b/pulp/apis/mipcl_api.py @@ -92,13 +92,13 @@ def actualSolve(self, lp): os.remove(tmpSol) except: pass - cmd = self.path - cmd += f" {tmpMps}" - cmd += f" -solfile {tmpSol}" + cmd = [self.path] + cmd.append(tmpMps) + cmd.extend(["-solfile", tmpSol]) if self.timeLimit is not None: - cmd += f" -time {self.timeLimit}" + cmd.extend(["-time", f"{self.timeLimit}"]) for option in self.options: - cmd += " " + option + cmd.append(option) if lp.isMIP(): if not self.mip: warnings.warn("MIPCL_CMD cannot solve the relaxation of a problem") @@ -107,7 +107,7 @@ def actualSolve(self, lp): else: pipe = open(os.devnull, "w") - return_code = subprocess.call(cmd.split(), stdout=pipe, stderr=pipe) + return_code = subprocess.call(cmd, stdout=pipe, stderr=pipe) # We need to undo the objective swap before finishing if lp.sense == constants.LpMaximize: lp += -lp.objective diff --git a/pulp/tests/test_pulp.py b/pulp/tests/test_pulp.py index 52abafbb..fae3930c 100644 --- a/pulp/tests/test_pulp.py +++ b/pulp/tests/test_pulp.py @@ -181,6 +181,27 @@ def test_continuous(self): prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) + def test_continuous_with_spaces_in_path(self): + with tempfile.TemporaryDirectory() as tmpdir: + prob = LpProblem(self._testMethodName, const.LpMinimize) + prob.tmpDir = tmpdir + "/folder with spaces" + x = LpVariable("x", 0, 4) + y = LpVariable("y", -1, 1) + z = LpVariable("z", 0) + w = LpVariable("w", 0) + 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" + + pulpTestCheck( + prob, + self.solver, + [const.LpStatusOptimal], + {x: 4, y: -1, z: 6, w: 0}, + ) + def test_non_intermediate_var(self): prob = LpProblem(self._testMethodName, const.LpMinimize) x_vars = {