From eef6431e65a5bf29b08b815720ec6a862cf32002 Mon Sep 17 00:00:00 2001 From: Franco Peschiera Date: Fri, 12 Jul 2024 10:50:38 +0200 Subject: [PATCH] added download of highs binary. And fixed errors in subprocess (#759) * added download of highs binary. And fixed errors in subprocess * remove the prints. Show available solvers * workaround to deactivate messages on licenses * make it compatible with windows * tests on windows. * linter * keep msg=True when detecting solvers since it's blocking the print of available solver. * more tests * undo hiding messages * clean comments --- .github/workflows/pythonpackage.yml | 8 +++ HISTORY | 5 ++ pulp/apis/__init__.py | 2 +- pulp/apis/coin_api.py | 4 +- pulp/apis/copt_api.py | 14 +++-- pulp/apis/gurobi_api.py | 3 +- pulp/apis/highs_api.py | 13 +++-- pulp/pulp.py | 7 +-- pulp/tests/run_tests.py | 4 ++ pulp/tests/test_pulp.py | 88 ++++------------------------- 10 files changed, 52 insertions(+), 96 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 7e7886f9..6cbe10f7 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -49,6 +49,14 @@ jobs: # alternatively if: contains(fromJSON("['3.7', '3.8', '3.9', '3.10', '3.11']"), matrix.python-version) run: | pip install highspy numpy + - name: Install highspy cmd + if: matrix.os == 'ubuntu-latest' + uses: supplypike/setup-bin@v4 + with: + uri: 'https://github.com/JuliaBinaryWrappers/HiGHSstatic_jll.jl/releases/download/HiGHSstatic-v1.7.1%2B0/HiGHSstatic.v1.7.1.x86_64-linux-gnu-cxx11.tar.gz' + subPath: 'bin' + name: 'highs' + version: '1.7.1' - name: Install coptpy run: | pip install coptpy diff --git a/HISTORY b/HISTORY index e236b327..0db4af1d 100644 --- a/HISTORY +++ b/HISTORY @@ -2,6 +2,11 @@ # Copyright S.A.Mitchell (s.mitchell@auckland.ac.nz), 2007- # Copyright F.Peschiera (pchtsp@gmail.com), 2019- # See the LICENSE file for copyright information. +2.9.0 2024-07-12 + HiGHS available as solver + added HiGHS_CMD to github actions + deactivated warnings on msg=False + minor fixes 2.8.0 2024-01-12 mip start in HiGHS_CMD and SCIP_PY GUROBI solver with environment handling diff --git a/pulp/apis/__init__.py b/pulp/apis/__init__.py index 41c3240f..5133a92a 100644 --- a/pulp/apis/__init__.py +++ b/pulp/apis/__init__.py @@ -151,7 +151,7 @@ def listSolvers(onlyAvailable=False): """ result = [] for s in _all_solvers: - solver = s() + solver = s(msg=False) if (not onlyAvailable) or solver.available(): result.append(solver.name) del solver diff --git a/pulp/apis/coin_api.py b/pulp/apis/coin_api.py index f0e68e6b..62191f9d 100644 --- a/pulp/apis/coin_api.py +++ b/pulp/apis/coin_api.py @@ -182,8 +182,10 @@ def solve_CBC(self, lp, use_mps=True): "Pulp: Error while trying to execute, use msg=True for more details" + self.path ) - if pipe: + try: pipe.close() + except: + pass if not os.path.exists(tmpSol): raise PulpSolverError("Pulp: Error while executing " + self.path) ( diff --git a/pulp/apis/copt_api.py b/pulp/apis/copt_api.py index f2631197..6b020b49 100644 --- a/pulp/apis/copt_api.py +++ b/pulp/apis/copt_api.py @@ -5,10 +5,15 @@ import warnings from uuid import uuid4 -from .core import sparse, ctypesArrayFill, PulpSolverError -from .core import clock, log - -from .core import LpSolver, LpSolver_CMD +from .core import ( + sparse, + ctypesArrayFill, + PulpSolverError, + LpSolver, + LpSolver_CMD, + clock, + operating_system, +) from ..constants import ( LpStatusNotSolved, LpStatusOptimal, @@ -894,7 +899,6 @@ def __init__( logPath=logPath, warmStart=warmStart, ) - self.coptenv = coptpy.Envr() self.coptmdl = self.coptenv.createModel() diff --git a/pulp/apis/gurobi_api.py b/pulp/apis/gurobi_api.py index ba8bf66d..514e10ac 100644 --- a/pulp/apis/gurobi_api.py +++ b/pulp/apis/gurobi_api.py @@ -440,7 +440,8 @@ def available(self): # normal execution return True # error: we display the gurobi message - warnings.warn(f"GUROBI error: {out}.") + if self.msg: + warnings.warn(f"GUROBI error: {out}.") return False def actualSolve(self, lp): diff --git a/pulp/apis/highs_api.py b/pulp/apis/highs_api.py index 4915217e..cbc0ea09 100644 --- a/pulp/apis/highs_api.py +++ b/pulp/apis/highs_api.py @@ -32,7 +32,7 @@ from typing import List from .core import LpSolver, LpSolver_CMD, subprocess, PulpSolverError -import os, sys +import os from .. import constants @@ -149,14 +149,19 @@ def actualSolve(self, lp): with open(tmpOptions, "w") as options_file: options_file.write("\n".join(file_options)) - process = subprocess.run(command, stdout=sys.stdout, stderr=sys.stderr) + # print(command) + process = subprocess.Popen(command, stdout=None, stderr=None) # HiGHS return code semantics (see: https://github.com/ERGO-Code/HiGHS/issues/527#issuecomment-946575028) # - -1: error # - 0: success # - 1: warning - if process.returncode == -1: - raise PulpSolverError("Error while executing HiGHS") + # process = subprocess.run(command, stdout=sys.stdout, stderr=sys.stderr) + if process.wait() == -1: + raise PulpSolverError( + "Pulp: Error while executing HiGHS, use msg=True for more details" + + self.path + ) with open(highs_log_file, "r") as log_file: lines = log_file.readlines() diff --git a/pulp/pulp.py b/pulp/pulp.py index 2c910934..b186e6ed 100644 --- a/pulp/pulp.py +++ b/pulp/pulp.py @@ -104,12 +104,7 @@ from . import constants as const from . import mps_lp as mpslp -try: - from collections.abc import Iterable -except ImportError: - # python 2.7 compatible - from collections.abc import Iterable - +from collections.abc import Iterable import logging log = logging.getLogger(__name__) diff --git a/pulp/tests/run_tests.py b/pulp/tests/run_tests.py index c6dd7647..0a6abd2a 100644 --- a/pulp/tests/run_tests.py +++ b/pulp/tests/run_tests.py @@ -4,6 +4,10 @@ def pulpTestAll(test_docs=False): + all_solvers = pulp.listSolvers(onlyAvailable=False) + available = pulp.listSolvers(onlyAvailable=True) + print(f"Available solvers: {available}") + print(f"Unavailable solvers: {set(all_solvers) - set(available)}") runner = unittest.TextTestRunner() suite_all = get_test_suite(test_docs) # we run all tests at the same time diff --git a/pulp/tests/test_pulp.py b/pulp/tests/test_pulp.py index 7093e5a3..d50de9e1 100644 --- a/pulp/tests/test_pulp.py +++ b/pulp/tests/test_pulp.py @@ -74,7 +74,7 @@ def dumpTestProblem(prob): prob.writeLP("debug.lp") prob.writeMPS("debug.mps") except: - print("(Failed to write the test problem.)") + pass class BaseSolverTest: @@ -84,7 +84,7 @@ class PuLPTest(unittest.TestCase): def setUp(self): self.solver = self.solveInst(msg=False) if not self.solver.available(): - self.skipTest(f"solver {self.solveInst} not available") + self.skipTest(f"solver {self.solveInst.name} not available") def tearDown(self): for ext in ["mst", "log", "lp", "mps", "sol"]: @@ -104,7 +104,6 @@ def test_variable_0_is_deleted(self): z = LpVariable("z", 0) c1 = x + y <= 5 c2 = c1 + z - z - print("\t Testing zero subtraction") assert str(c2) assert c2[z] == 0 @@ -122,7 +121,6 @@ def test_infeasible(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing inconsistent lp solution") # this was a problem with use_mps=false if self.solver.__class__ in [PULP_CBC_CMD, COIN_CMD]: pulpTestCheck( @@ -157,7 +155,6 @@ def test_continuous(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing continuous LP solution") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) @@ -173,7 +170,6 @@ def test_continuous_max(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing maximize continuous LP solution") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: 1, z: 8, w: 0} ) @@ -189,7 +185,6 @@ def test_unbounded(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing unbounded continuous LP solution") if self.solver.__class__ in [GUROBI, CPLEX_CMD, YAPOSIB, MOSEK, COPT]: # These solvers report infeasible or unbounded pulpTestCheck( @@ -200,7 +195,6 @@ def test_unbounded(self): elif self.solver.__class__ in [COINMP_DLL, MIPCL_CMD]: # COINMP_DLL is just plain wrong # also MIPCL_CMD - print("\t\t Error in CoinMP and MIPCL_CMD: reports Optimal") pulpTestCheck(prob, self.solver, [const.LpStatusOptimal]) elif self.solver.__class__ is GLPK_CMD: # GLPK_CMD Does not report unbounded problems, correctly @@ -208,8 +202,9 @@ def test_unbounded(self): elif self.solver.__class__ in [GUROBI_CMD, SCIP_CMD, FSCIP_CMD, SCIP_PY]: # GUROBI_CMD has a very simple interface pulpTestCheck(prob, self.solver, [const.LpStatusNotSolved]) - elif self.solver.__class__ in [CHOCO_CMD]: + elif self.solver.__class__ in [CHOCO_CMD, HiGHS_CMD]: # choco bounds all variables. Would not return unbounded status + # highs_cmd is inconsistent pass else: pulpTestCheck(prob, self.solver, [const.LpStatusUnbounded]) @@ -225,7 +220,6 @@ def test_long_var_name(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing Long Names") if self.solver.__class__ in [ CPLEX_CMD, GLPK_CMD, @@ -268,7 +262,6 @@ def test_repeated_name(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing repeated Names") if self.solver.__class__ in [ COIN_CMD, COINMP_DLL, @@ -319,7 +312,6 @@ def test_zero_constraint(self): prob += -y + z == 7, "c3" prob += w >= 0, "c4" prob += lpSum([0, 0]) <= 0, "c5" - print("\t Testing zero constraint") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) @@ -335,7 +327,6 @@ def test_no_objective(self): prob += -y + z == 7, "c3" prob += w >= 0, "c4" prob += lpSum([0, 0]) <= 0, "c5" - print("\t Testing zero objective") pulpTestCheck(prob, self.solver, [const.LpStatusOptimal]) def test_variable_as_objective(self): @@ -350,7 +341,6 @@ def test_variable_as_objective(self): prob += -y + z == 7, "c3" prob += w >= 0, "c4" prob += lpSum([0, 0]) <= 0, "c5" - print("\t Testing LpVariable (not LpAffineExpression) objective") pulpTestCheck(prob, self.solver, [const.LpStatusOptimal]) def test_longname_lp(self): @@ -365,7 +355,6 @@ def test_longname_lp(self): prob += -y + z == 7, "c3" prob += w >= 0, "c4" if self.solver.__class__ in [PULP_CBC_CMD, COIN_CMD]: - print("\t Testing Long lines in LP") pulpTestCheck( prob, self.solver, @@ -385,7 +374,6 @@ def test_divide(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing LpAffineExpression divide") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) @@ -399,7 +387,6 @@ def test_mip(self): prob += x + y <= 5, "c1" prob += x + z >= 10, "c2" prob += -y + z == 7.5, "c3" - print("\t Testing MIP solution") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 3, y: -0.5, z: 7} ) @@ -413,7 +400,6 @@ def test_mip_floats_objective(self): prob += x + y <= 5, "c1" prob += x + z >= 10, "c2" prob += -y + z == 7.5, "c3" - print("\t Testing MIP solution with floats in objective") pulpTestCheck( prob, self.solver, @@ -443,7 +429,6 @@ def test_initial_value(self): "HiGHS_CMD", ]: self.solver.optionsDict["warmStart"] = True - print("\t Testing Initial value in MIP solution") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 3, y: -0.5, z: 7} ) @@ -462,7 +447,6 @@ def test_fixed_value(self): v.setInitialValue(solution[v]) v.fixValue() self.solver.optionsDict["warmStart"] = True - print("\t Testing fixing value in MIP solution") pulpTestCheck(prob, self.solver, [const.LpStatusOptimal], solution) def test_relaxed_mip(self): @@ -475,7 +459,6 @@ def test_relaxed_mip(self): prob += x + z >= 10, "c2" prob += -y + z == 7.5, "c3" self.solver.mip = 0 - print("\t Testing MIP relaxation") if self.solver.__class__ in [ GUROBI_CMD, CHOCO_CMD, @@ -501,7 +484,6 @@ def test_feasibility_only(self): prob += x + y <= 5, "c1" prob += x + z >= 10, "c2" prob += -y + z == 7.5, "c3" - print("\t Testing feasibility problem (no objective)") pulpTestCheck(prob, self.solver, [const.LpStatusOptimal]) def test_infeasible_2(self): @@ -512,7 +494,6 @@ def test_infeasible_2(self): prob += x + y <= 5.2, "c1" prob += x + z >= 10.3, "c2" prob += -y + z == 17.5, "c3" - print("\t Testing an infeasible problem") if self.solver.__class__ is GLPK_CMD: # GLPK_CMD return codes are not informative enough pulpTestCheck(prob, self.solver, [const.LpStatusUndefined]) @@ -530,7 +511,6 @@ def test_integer_infeasible(self): prob += x + y <= 5.2, "c1" prob += x + z >= 10.3, "c2" prob += -y + z == 7.4, "c3" - print("\t Testing an integer infeasible problem") if self.solver.__class__ in [GLPK_CMD, COIN_CMD, PULP_CBC_CMD, MOSEK]: # GLPK_CMD returns InfeasibleOrUnbounded pulpTestCheck( @@ -541,7 +521,6 @@ def test_integer_infeasible(self): elif self.solver.__class__ in [COINMP_DLL]: # Currently there is an error in COINMP for problems where # presolve eliminates too many variables - print("\t\t Error in CoinMP to be fixed, reports Optimal") pulpTestCheck(prob, self.solver, [const.LpStatusOptimal]) elif self.solver.__class__ in [GUROBI_CMD, FSCIP_CMD]: pulpTestCheck(prob, self.solver, [const.LpStatusNotSolved]) @@ -558,7 +537,6 @@ def test_integer_infeasible_2(self): prob += dummy prob += c1 + c2 == 2 prob += c1 <= 0 - print("\t Testing another integer infeasible problem") if self.solver.__class__ in [GUROBI_CMD, SCIP_CMD, FSCIP_CMD, SCIP_PY]: pulpTestCheck(prob, self.solver, [const.LpStatusNotSolved]) elif self.solver.__class__ in [GLPK_CMD]: @@ -587,7 +565,6 @@ def test_column_based(self): x = LpVariable("x", 0, 4, const.LpContinuous, obj + a + b) y = LpVariable("y", -1, 1, const.LpContinuous, 4 * obj + a - c) z = LpVariable("z", 0, None, const.LpContinuous, 9 * obj + b + c) - print("\t Testing column based modelling") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6} ) @@ -609,7 +586,6 @@ def test_colum_based_empty_constraints(self): y = LpVariable("y", -1, 1, const.LpContinuous, 4 * obj - c) z = LpVariable("z", 0, None, const.LpContinuous, 9 * obj + b + c) if self.solver.__class__ in [CPLEX_CMD, COINMP_DLL, YAPOSIB, PYGLPK]: - print("\t Testing column based modelling with empty constraints") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6} ) @@ -638,7 +614,6 @@ def test_dual_variables_reduced_costs(self): YAPOSIB, PYGLPK, ]: - print("\t Testing dual variables and slacks reporting") pulpTestCheck( prob, self.solver, @@ -668,7 +643,6 @@ def test_column_based_modelling_resolve(self): prob.resolve() z = LpVariable("z", 0, None, const.LpContinuous, 9 * obj + b + c) if self.solver.__class__ in [COINMP_DLL]: - print("\t Testing resolve of problem") prob.resolve() # difficult to check this is doing what we want as the resolve is # overridden if it is not implemented @@ -689,7 +663,6 @@ def test_sequential_solve(self): prob += x <= 1, "c1" if self.solver.__class__ in [COINMP_DLL, GUROBI]: - print("\t Testing Sequential Solves") status = prob.sequentialSolve([obj1, obj2], solver=self.solver) pulpTestCheck( prob, @@ -714,7 +687,6 @@ def test_fractional_constraints(self): prob += -y + z == 7, "c3" prob += w >= 0, "c4" prob += LpFractionConstraint(x, z, const.LpConstraintEQ, 0.5, name="c5") - print("\t Testing fractional constraints") pulpTestCheck( prob, self.solver, @@ -736,7 +708,6 @@ def test_elastic_constraints(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob.extend((w >= -1).makeElasticSubProblem()) - print("\t Testing elastic constraints (no change)") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: -1} ) @@ -755,7 +726,6 @@ def test_elastic_constraints_2(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob.extend((w >= -1).makeElasticSubProblem(proportionFreeBound=0.1)) - print("\t Testing elastic constraints (freebound)") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: -1.1} ) @@ -774,7 +744,6 @@ def test_elastic_constraints_penalty_unchanged(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob.extend((w >= -1).makeElasticSubProblem(penalty=1.1)) - print("\t Testing elastic constraints (penalty unchanged)") pulpTestCheck( prob, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: -1.0} ) @@ -793,7 +762,6 @@ def test_elastic_constraints_penalty_unbounded(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob.extend((w >= -1).makeElasticSubProblem(penalty=0.9)) - print("\t Testing elastic constraints (penalty unbounded)") if self.solver.__class__ in [ COINMP_DLL, GUROBI, @@ -836,19 +804,17 @@ def test_msg_arg(self): data = prob.toDict() var1, prob1 = LpProblem.fromDict(data) x, y, z, w = (var1[name] for name in ["x", "y", "z", "w"]) - if self.solver.name in ["HiGHS"]: - # HiGHS has issues with displaying output in Ubuntu - return - self.solver.msg = True pulpTestCheck( - prob1, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} + prob1, + self.solveInst(msg=True), + [const.LpStatusOptimal], + {x: 4, y: -1, z: 6, w: 0}, ) def test_pulpTestAll(self): """ Test the availability of the function pulpTestAll """ - print("\t Testing the availability of the function pulpTestAll") from pulp import pulpTestAll def test_export_dict_LP(self): @@ -865,7 +831,6 @@ def test_export_dict_LP(self): data = prob.toDict() var1, prob1 = LpProblem.fromDict(data) x, y, z, w = (var1[name] for name in ["x", "y", "z", "w"]) - print("\t Testing continuous LP solution - export dict") pulpTestCheck( prob1, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) @@ -883,7 +848,6 @@ def test_export_dict_LP_no_obj(self): data = prob.toDict() var1, prob1 = LpProblem.fromDict(data) x, y, z, w = (var1[name] for name in ["x", "y", "z", "w"]) - print("\t Testing export dict for LP") pulpTestCheck( prob1, self.solver, [const.LpStatusOptimal], {x: 4, y: 1, z: 6, w: 0} ) @@ -908,7 +872,6 @@ def test_export_json_LP(self): except: pass x, y, z, w = (var1[name] for name in ["x", "y", "z", "w"]) - print("\t Testing continuous LP solution - export JSON") pulpTestCheck( prob1, self.solver, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) @@ -928,7 +891,6 @@ def test_export_dict_MIP(self): data_backup = copy.deepcopy(data) var1, prob1 = LpProblem.fromDict(data) x, y, z = (var1[name] for name in ["x", "y", "z"]) - print("\t Testing export dict MIP") pulpTestCheck( prob1, self.solver, [const.LpStatusOptimal], {x: 3, y: -0.5, z: 7} ) @@ -949,7 +911,6 @@ def test_export_dict_max(self): data = prob.toDict() var1, prob1 = LpProblem.fromDict(data) x, y, z, w = (var1[name] for name in ["x", "y", "z", "w"]) - print("\t Testing maximize continuous LP solution") pulpTestCheck( prob1, self.solver, [const.LpStatusOptimal], {x: 4, y: 1, z: 8, w: 0} ) @@ -967,7 +928,6 @@ def test_export_solver_dict_LP(self): prob += w >= 0, "c4" data = self.solver.toDict() solver1 = getSolverFromDict(data) - print("\t Testing continuous LP solution - export solver dict") pulpTestCheck( prob, solver1, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) @@ -1007,7 +967,6 @@ def test_export_solver_json(self): os.remove(filename) except: pass - print("\t Testing continuous LP solution - export solver JSON") pulpTestCheck( prob, solver1, [const.LpStatusOptimal], {x: 4, y: -1, z: 6, w: 0} ) @@ -1026,7 +985,6 @@ def test_timeLimit(self): prob += w >= 0, "c4" self.solver.timeLimit = 20 # CHOCO has issues when given a time limit - print("\t Testing timeLimit argument") if self.solver.name != "CHOCO_CMD": pulpTestCheck( prob, @@ -1036,7 +994,6 @@ def test_timeLimit(self): ) def test_assignInvalidStatus(self): - print("\t Testing invalid status") t = LpProblem("test") Invalid = -100 self.assertRaises(const.PulpError, lambda: t.assignStatus(Invalid)) @@ -1064,7 +1021,6 @@ def test_logPath(self): "PULP_CBC_CMD", "COIN_CMD", ]: - print("\t Testing logPath argument") pulpTestCheck( prob, self.solver, @@ -1085,7 +1041,6 @@ def test_makeDict_behavior(self): target = {"A": {"C": 1, "D": 2}, "B": {"C": 3, "D": 4}} dict_with_default = makeDict(headers, values, default=0) dict_without_default = makeDict(headers, values) - print("\t Testing makeDict general behavior") self.assertEqual(dict_with_default, target) self.assertEqual(dict_without_default, target) @@ -1097,7 +1052,6 @@ def test_makeDict_default_value(self): values = [[1, 2], [3, 4]] dict_with_default = makeDict(headers, values, default=0) dict_without_default = makeDict(headers, values) - print("\t Testing makeDict default value behavior") # Check if a default value is passed self.assertEqual(dict_with_default["X"]["Y"], 0) # Check if a KeyError is raised @@ -1121,7 +1075,6 @@ def test_importMPS_maximize(self): _vars, prob2 = LpProblem.fromMPS(filename, sense=prob.sense) _dict1 = getSortedDict(prob) _dict2 = getSortedDict(prob2) - print("\t Testing reading MPS files - maximize") self.assertDictEqual(_dict1, _dict2) def test_importMPS_noname(self): @@ -1141,7 +1094,6 @@ def test_importMPS_noname(self): _vars, prob2 = LpProblem.fromMPS(filename, sense=prob.sense) _dict1 = getSortedDict(prob) _dict2 = getSortedDict(prob2) - print("\t Testing reading MPS files - noname") self.assertDictEqual(_dict1, _dict2) def test_importMPS_integer(self): @@ -1159,7 +1111,6 @@ def test_importMPS_integer(self): _vars, prob2 = LpProblem.fromMPS(filename, sense=prob.sense) _dict1 = getSortedDict(prob) _dict2 = getSortedDict(prob2) - print("\t Testing reading MPS files - integer variable") self.assertDictEqual(_dict1, _dict2) def test_importMPS_binary(self): @@ -1178,7 +1129,6 @@ def test_importMPS_binary(self): ) _dict1 = getSortedDict(prob, keyCons="constant") _dict2 = getSortedDict(prob2, keyCons="constant") - print("\t Testing reading MPS files - binary variable, no constraint names") self.assertDictEqual(_dict1, _dict2) def test_importMPS_RHS_fields56(self): @@ -1193,20 +1143,10 @@ def test_importMPS_PL_bound(self): """Import MPS file with PL bound type.""" with tempfile.NamedTemporaryFile(delete=False) as h: h.write(str.encode(EXAMPLE_MPS_PL_BOUNDS)) - print("\t Testing reading MPS files - PL bound") _, problem = LpProblem.fromMPS(h.name) os.unlink(h.name) self.assertIsInstance(problem, LpProblem) - # def test_importMPS_2(self): - # name = self._testMethodName - # # filename = name + ".mps" - # filename = "/home/pchtsp/Downloads/test.mps" - # _vars, _prob = LpProblem.fromMPS(filename) - # _prob.solve() - # for k, v in _vars.items(): - # print(k, v.value()) - def test_unset_objective_value__is_valid(self): """Given a valid problem that does not converge, assert that it is still categorised as valid. @@ -1267,7 +1207,6 @@ def add_const(prob): @gurobi_test def test_measuring_solving_time(self): - print("\t Testing measuring optimization time") time_limit = 10 solver_settings = dict( @@ -1278,6 +1217,7 @@ def test_measuring_solving_time(self): CPLEX_CMD=50, GUROBI=50, HiGHS=50, + HiGHS_CMD=50, ) bins = solver_settings.get(self.solver.name) if bins is None: @@ -1305,10 +1245,9 @@ def test_measuring_solving_time(self): @gurobi_test def test_time_limit_no_solution(self): - print("\t Test time limit with no solution") time_limit = 1 - solver_settings = dict(HiGHS=50, PULP_CBC_CMD=30, COIN_CMD=30) + solver_settings = dict(HiGHS_CMD=60, HiGHS=60, PULP_CBC_CMD=60, COIN_CMD=60) bins = solver_settings.get(self.solver.name) if bins is None: # not all solvers have timeLimit support @@ -1331,7 +1270,6 @@ def test_invalid_var_names(self): prob += x + z >= 10, "c2" prob += -y + z == 7, "c3" prob += w >= 0, "c4" - print("\t Testing invalid var names") if self.solver.name not in [ "GUROBI_CMD", # end is a key-word for LP files ]: @@ -1349,7 +1287,6 @@ def test_LpVariable_indexs_param(self): customers = [1, 2, 3] agents = ["A", "B", "C"] - print("\t Testing 'indexs' param continues to work for LpVariable.dicts") # explicit param creates a dict of type LpVariable assign_vars = LpVariable.dicts(name="test", indices=(customers, agents)) for k, v in assign_vars.items(): @@ -1362,7 +1299,6 @@ def test_LpVariable_indexs_param(self): for a, b in v.items(): self.assertIsInstance(b, LpVariable) - print("\t Testing 'indexs' param continues to work for LpVariable.matrix") # explicit param creates list of LpVariable assign_vars_matrix = LpVariable.matrix( name="test", indices=(customers, agents) @@ -1385,14 +1321,12 @@ def test_LpVariable_indices_param(self): customers = [1, 2, 3] agents = ["A", "B", "C"] - print("\t Testing 'indices' argument works in LpVariable.dicts") # explicit param creates a dict of type LpVariable assign_vars = LpVariable.dicts(name="test", indices=(customers, agents)) for k, v in assign_vars.items(): for a, b in v.items(): self.assertIsInstance(b, LpVariable) - print("\t Testing 'indices' param continues to work for LpVariable.matrix") # explicit param creates list of list of LpVariable assign_vars_matrix = LpVariable.matrix( name="test", indices=(customers, agents) @@ -1407,7 +1341,6 @@ def test_parse_cplex_mipopt_solution(self): """ from io import StringIO - print("\t Testing that `readsol` can parse CPLEX mipopt solution") # Example solution generated by CPLEX mipopt solver file_content = """ @@ -1472,7 +1405,6 @@ def test_options_parsing_SCIP_HIGHS(self): prob += -y + z == 7, "c3" prob += w >= 0, "c4" # CHOCO has issues when given a time limit - print("\t Testing options parsing") if self.solver.__class__ in [SCIP_CMD, FSCIP_CMD]: self.solver.options = ["limits/time", 20] pulpTestCheck(