PuLP 3.3.0__tar.gz → 3.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {pulp-3.3.0 → pulp-3.3.1}/PKG-INFO +6 -14
  2. {pulp-3.3.0 → pulp-3.3.1}/PuLP.egg-info/PKG-INFO +6 -14
  3. {pulp-3.3.0 → pulp-3.3.1}/PuLP.egg-info/requires.txt +2 -3
  4. {pulp-3.3.0 → pulp-3.3.1}/README.rst +3 -10
  5. {pulp-3.3.0 → pulp-3.3.1}/pulp/__init__.py +25 -0
  6. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/coin_api.py +7 -7
  7. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/copt_api.py +10 -10
  8. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/core.py +4 -4
  9. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/cplex_api.py +15 -7
  10. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/cuopt_api.py +5 -5
  11. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/glpk_api.py +7 -8
  12. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/gurobi_api.py +6 -6
  13. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/highs_api.py +9 -6
  14. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/mosek_api.py +7 -7
  15. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/scip_api.py +4 -4
  16. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/xpress_api.py +7 -7
  17. {pulp-3.3.0 → pulp-3.3.1}/pulp/constants.py +2 -1
  18. {pulp-3.3.0 → pulp-3.3.1}/pulp/mps_lp.py +6 -6
  19. {pulp-3.3.0 → pulp-3.3.1}/pulp/pulp.py +410 -74
  20. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/bin_packing_problem.py +2 -2
  21. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/test_gurobipy_env.py +4 -4
  22. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/test_lpdot.py +7 -5
  23. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/test_pulp.py +370 -291
  24. {pulp-3.3.0 → pulp-3.3.1}/pyproject.toml +2 -2
  25. {pulp-3.3.0 → pulp-3.3.1}/LICENSE +0 -0
  26. {pulp-3.3.0 → pulp-3.3.1}/PuLP.egg-info/SOURCES.txt +0 -0
  27. {pulp-3.3.0 → pulp-3.3.1}/PuLP.egg-info/dependency_links.txt +0 -0
  28. {pulp-3.3.0 → pulp-3.3.1}/PuLP.egg-info/entry_points.txt +0 -0
  29. {pulp-3.3.0 → pulp-3.3.1}/PuLP.egg-info/top_level.txt +0 -0
  30. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/__init__.py +0 -0
  31. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/choco_api.py +0 -0
  32. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/mipcl_api.py +0 -0
  33. {pulp-3.3.0 → pulp-3.3.1}/pulp/apis/sas_api.py +0 -0
  34. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/__init__.py +0 -0
  35. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/arm64/__init__.py +0 -0
  36. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/arm64/cbc +0 -0
  37. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/arm64/coin-license.txt +0 -0
  38. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i32/__init__.py +0 -0
  39. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i32/cbc +0 -0
  40. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i32/coin-license.txt +0 -0
  41. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i64/__init__.py +0 -0
  42. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i64/cbc +0 -0
  43. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i64/coin-license.txt +0 -0
  44. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/osx/i64/__init__.py +0 -0
  45. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/osx/i64/cbc +0 -0
  46. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/osx/i64/coin-license.txt +0 -0
  47. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/win/i32/__init__.py +0 -0
  48. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/win/i32/cbc.exe +0 -0
  49. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/win/i32/coin-license.txt +0 -0
  50. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/win/i64/__init__.py +0 -0
  51. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/win/i64/cbc.exe +0 -0
  52. {pulp-3.3.0 → pulp-3.3.1}/pulp/solverdir/cbc/win/i64/coin-license.txt +0 -0
  53. {pulp-3.3.0 → pulp-3.3.1}/pulp/sparse.py +0 -0
  54. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/__init__.py +0 -0
  55. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/run_tests.py +0 -0
  56. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/test_examples.py +0 -0
  57. {pulp-3.3.0 → pulp-3.3.1}/pulp/tests/test_sparse.py +0 -0
  58. {pulp-3.3.0 → pulp-3.3.1}/pulp/utilities.py +0 -0
  59. {pulp-3.3.0 → pulp-3.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PuLP
3
- Version: 3.3.0
3
+ Version: 3.3.1
4
4
  Summary: PuLP is an LP modeler written in python. PuLP can generate MPS or LP files and call GLPK, COIN CLP/CBC, CPLEX, and GUROBI to solve linear problems.
5
5
  Author: J.S. Roy
6
6
  Author-email: "S.A. Mitchell" <pulp@stuartmitchell.com>, Franco Peschiera <pchtsp@gmail.com>
@@ -19,8 +19,7 @@ Requires-Python: >=3.9
19
19
  Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE
21
21
  Provides-Extra: open-py
22
- Requires-Dist: cylp; extra == "open-py"
23
- Requires-Dist: highspy; extra == "open-py"
22
+ Requires-Dist: highspy==1.13; extra == "open-py"
24
23
  Requires-Dist: pyscipopt; extra == "open-py"
25
24
  Provides-Extra: public-py
26
25
  Requires-Dist: gurobipy; extra == "public-py"
@@ -28,7 +27,7 @@ Requires-Dist: coptpy; extra == "public-py"
28
27
  Requires-Dist: xpress; extra == "public-py"
29
28
  Requires-Dist: cplex; (sys_platform != "darwin" or python_full_version < "3.12") and extra == "public-py"
30
29
  Provides-Extra: highs
31
- Requires-Dist: highspy; extra == "highs"
30
+ Requires-Dist: highspy==1.13; extra == "highs"
32
31
  Provides-Extra: scip
33
32
  Requires-Dist: pyscipopt; extra == "scip"
34
33
  Provides-Extra: gurobi
@@ -84,7 +83,6 @@ PuLP can use a variety of solvers. The default solver is the COIN-OR CBC solver,
84
83
  python -m pip install pulp[highs]
85
84
  python -m pip install pulp[copt]
86
85
  python -m pip install pulp[mosek]
87
- python -m pip install pulp[cylp]
88
86
 
89
87
 
90
88
  If you want to install all open source solvers (scip, highs, cylp), you can use the shortcut::
@@ -95,18 +93,12 @@ For more information on how to install solvers, see the `guide on configuring so
95
93
  Quickstart
96
94
  ===============
97
95
 
98
- Use ``LpVariable`` to create new variables. To create a variable x with 0 ≤ x ≤ 3::
96
+ Create a problem first, then add variables with ``add_variable`` (variables are owned by the model). To create a variable ``x`` with 0 ≤ x ≤ 3 and a binary variable ``y``::
99
97
 
100
98
  from pulp import *
101
- x = LpVariable("x", 0, 3)
102
-
103
- To create a binary variable, y, with values either 0 or 1::
104
-
105
- y = LpVariable("y", cat="Binary")
106
-
107
- Use ``LpProblem`` to create new problems. Create a problem called "myProblem" like so::
108
-
109
99
  prob = LpProblem("myProblem", LpMinimize)
100
+ x = prob.add_variable("x", 0, 3)
101
+ y = prob.add_variable("y", cat="Binary")
110
102
 
111
103
  Combine variables in order to create expressions and constraints, and then add them to the problem.::
112
104
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PuLP
3
- Version: 3.3.0
3
+ Version: 3.3.1
4
4
  Summary: PuLP is an LP modeler written in python. PuLP can generate MPS or LP files and call GLPK, COIN CLP/CBC, CPLEX, and GUROBI to solve linear problems.
5
5
  Author: J.S. Roy
6
6
  Author-email: "S.A. Mitchell" <pulp@stuartmitchell.com>, Franco Peschiera <pchtsp@gmail.com>
@@ -19,8 +19,7 @@ Requires-Python: >=3.9
19
19
  Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE
21
21
  Provides-Extra: open-py
22
- Requires-Dist: cylp; extra == "open-py"
23
- Requires-Dist: highspy; extra == "open-py"
22
+ Requires-Dist: highspy==1.13; extra == "open-py"
24
23
  Requires-Dist: pyscipopt; extra == "open-py"
25
24
  Provides-Extra: public-py
26
25
  Requires-Dist: gurobipy; extra == "public-py"
@@ -28,7 +27,7 @@ Requires-Dist: coptpy; extra == "public-py"
28
27
  Requires-Dist: xpress; extra == "public-py"
29
28
  Requires-Dist: cplex; (sys_platform != "darwin" or python_full_version < "3.12") and extra == "public-py"
30
29
  Provides-Extra: highs
31
- Requires-Dist: highspy; extra == "highs"
30
+ Requires-Dist: highspy==1.13; extra == "highs"
32
31
  Provides-Extra: scip
33
32
  Requires-Dist: pyscipopt; extra == "scip"
34
33
  Provides-Extra: gurobi
@@ -84,7 +83,6 @@ PuLP can use a variety of solvers. The default solver is the COIN-OR CBC solver,
84
83
  python -m pip install pulp[highs]
85
84
  python -m pip install pulp[copt]
86
85
  python -m pip install pulp[mosek]
87
- python -m pip install pulp[cylp]
88
86
 
89
87
 
90
88
  If you want to install all open source solvers (scip, highs, cylp), you can use the shortcut::
@@ -95,18 +93,12 @@ For more information on how to install solvers, see the `guide on configuring so
95
93
  Quickstart
96
94
  ===============
97
95
 
98
- Use ``LpVariable`` to create new variables. To create a variable x with 0 ≤ x ≤ 3::
96
+ Create a problem first, then add variables with ``add_variable`` (variables are owned by the model). To create a variable ``x`` with 0 ≤ x ≤ 3 and a binary variable ``y``::
99
97
 
100
98
  from pulp import *
101
- x = LpVariable("x", 0, 3)
102
-
103
- To create a binary variable, y, with values either 0 or 1::
104
-
105
- y = LpVariable("y", cat="Binary")
106
-
107
- Use ``LpProblem`` to create new problems. Create a problem called "myProblem" like so::
108
-
109
99
  prob = LpProblem("myProblem", LpMinimize)
100
+ x = prob.add_variable("x", 0, 3)
101
+ y = prob.add_variable("y", cat="Binary")
110
102
 
111
103
  Combine variables in order to create expressions and constraints, and then add them to the problem.::
112
104
 
@@ -11,14 +11,13 @@ cplex
11
11
  gurobipy
12
12
 
13
13
  [highs]
14
- highspy
14
+ highspy==1.13
15
15
 
16
16
  [mosek]
17
17
  mosek
18
18
 
19
19
  [open_py]
20
- cylp
21
- highspy
20
+ highspy==1.13
22
21
  pyscipopt
23
22
 
24
23
  [public_py]
@@ -39,7 +39,6 @@ PuLP can use a variety of solvers. The default solver is the COIN-OR CBC solver,
39
39
  python -m pip install pulp[highs]
40
40
  python -m pip install pulp[copt]
41
41
  python -m pip install pulp[mosek]
42
- python -m pip install pulp[cylp]
43
42
 
44
43
 
45
44
  If you want to install all open source solvers (scip, highs, cylp), you can use the shortcut::
@@ -50,18 +49,12 @@ For more information on how to install solvers, see the `guide on configuring so
50
49
  Quickstart
51
50
  ===============
52
51
 
53
- Use ``LpVariable`` to create new variables. To create a variable x with 0 ≤ x ≤ 3::
52
+ Create a problem first, then add variables with ``add_variable`` (variables are owned by the model). To create a variable ``x`` with 0 ≤ x ≤ 3 and a binary variable ``y``::
54
53
 
55
54
  from pulp import *
56
- x = LpVariable("x", 0, 3)
57
-
58
- To create a binary variable, y, with values either 0 or 1::
59
-
60
- y = LpVariable("y", cat="Binary")
61
-
62
- Use ``LpProblem`` to create new problems. Create a problem called "myProblem" like so::
63
-
64
55
  prob = LpProblem("myProblem", LpMinimize)
56
+ x = prob.add_variable("x", 0, 3)
57
+ y = prob.add_variable("y", cat="Binary")
65
58
 
66
59
  Combine variables in order to create expressions and constraints, and then add them to the problem.::
67
60
 
@@ -29,6 +29,31 @@ Module file that imports all of the pulp functions
29
29
 
30
30
  Copyright 2007- Stuart Mitchell (s.mitchell@auckland.ac.nz)
31
31
  """
32
+
33
+ # Pre-load highspy's C extension with RTLD_GLOBAL before any other imports.
34
+ # highspy._core uses C++ exception handling (__cxa_throw etc.) that must be globally
35
+ # visible when pulp._rustcore (a Rust/pyo3 extension) is later loaded into the same
36
+ # process. Without RTLD_GLOBAL, the C++ runtime state gets split across multiple
37
+ # symbol scopes, causing a segfault when highspy.Highs.run() is called.
38
+ # We use find_spec("highspy") (the package) not find_spec("highspy._core") (the
39
+ # submodule) to avoid triggering highspy/__init__.py which would import _core with
40
+ # RTLD_LOCAL before our explicit RTLD_GLOBAL call can take effect.
41
+ try:
42
+ import ctypes as _ctypes
43
+ import glob as _glob
44
+ import importlib.util as _ilu
45
+
46
+ _pkg_spec = _ilu.find_spec("highspy")
47
+ if _pkg_spec is not None and _pkg_spec.submodule_search_locations:
48
+ for _loc in _pkg_spec.submodule_search_locations:
49
+ for _so in _glob.glob(_loc + "/_core*.so"):
50
+ _ctypes.CDLL(_so, _ctypes.RTLD_GLOBAL)
51
+ break
52
+ del _ctypes, _glob, _ilu, _pkg_spec
53
+ except Exception:
54
+ pass
55
+
56
+
32
57
  from .constants import VERSION
33
58
 
34
59
  from .pulp import *
@@ -167,7 +167,7 @@ class COIN_CMD(LpSolver_CMD):
167
167
  vs = lp.writeLP(tmpLp)
168
168
  # In the Lp we do not create new variable or constraint names:
169
169
  variablesNames = {v.name: v.name for v in vs}
170
- constraintsNames = {c: c for c in lp.constraints}
170
+ constraintsNames = {c: c for c in lp._constraints}
171
171
  cmds = " " + tmpLp + " "
172
172
  if self.optionsDict.get("warmStart", False):
173
173
  self.writesol(tmpMst, lp, vs, variablesNames, constraintsNames)
@@ -323,7 +323,7 @@ class COIN_CMD(LpSolver_CMD):
323
323
  returns status, values, reducedCosts, shadowPrices, slacks, sol_status
324
324
  """
325
325
  variablesNames = {v.name: v.name for v in vs}
326
- constraintsNames = {c: c for c in lp.constraints}
326
+ constraintsNames = {c: c for c in lp._constraints}
327
327
  return self.readsol_MPS(filename, lp, vs, variablesNames, constraintsNames)
328
328
 
329
329
  def get_status(self, filename):
@@ -758,7 +758,7 @@ class YAPOSIB(LpSolver):
758
758
  var.varValue = var.solverVar.solution
759
759
  var.dj = var.solverVar.reducedcost
760
760
  # put pi and slack variables against the constraints
761
- for constr in lp.constraints.values():
761
+ for constr in lp._constraints.values():
762
762
  constr.pi = constr.solverConstraint.dual
763
763
  constr.slack = -constr.constant - constr.solverConstraint.activity
764
764
  if self.msg:
@@ -817,7 +817,7 @@ class YAPOSIB(LpSolver):
817
817
  prob.obj[col.index] = lp.objective.get(var, 0.0)
818
818
  var.solverVar = col
819
819
  log.debug("add the Constraints to the problem")
820
- for name, constraint in lp.constraints.items():
820
+ for name, constraint in lp._constraints.items():
821
821
  row = prob.rows.add(
822
822
  yaposib.vec(
823
823
  [
@@ -853,7 +853,7 @@ class YAPOSIB(LpSolver):
853
853
  solutionStatus = self.findSolutionValues(lp)
854
854
  for var in lp.variables():
855
855
  var.modified = False
856
- for constraint in lp.constraints.values():
856
+ for constraint in lp._constraints.values():
857
857
  constraint.modified = False
858
858
  return solutionStatus
859
859
 
@@ -865,7 +865,7 @@ class YAPOSIB(LpSolver):
865
865
  constraints
866
866
  """
867
867
  log.debug("Resolve the model using yaposib")
868
- for constraint in lp.constraints.values():
868
+ for constraint in lp._constraints.values():
869
869
  row = constraint.solverConstraint
870
870
  if constraint.modified:
871
871
  if constraint.sense == constants.LpConstraintLE:
@@ -882,7 +882,7 @@ class YAPOSIB(LpSolver):
882
882
  solutionStatus = self.findSolutionValues(lp)
883
883
  for var in lp.variables():
884
884
  var.modified = False
885
- for constraint in lp.constraints.values():
885
+ for constraint in lp._constraints.values():
886
886
  constraint.modified = False
887
887
  return solutionStatus
888
888
 
@@ -471,7 +471,7 @@ class COPT_DLL(LpSolver):
471
471
  """
472
472
  cols = list(lp.variables())
473
473
  ncol = len(cols)
474
- nrow = len(lp.constraints)
474
+ nrow = len(lp._constraints)
475
475
 
476
476
  collb = (ctypes.c_double * ncol)()
477
477
  colub = (ctypes.c_double * ncol)()
@@ -525,9 +525,9 @@ class COPT_DLL(LpSolver):
525
525
 
526
526
  # Extract constraint rhs, senses and names
527
527
  idx = 0
528
- for row in lp.constraints:
529
- rowrhs[idx] = -lp.constraints[row].constant
530
- rowsense[idx] = coptrsense[lp.constraints[row].sense]
528
+ for row in lp._constraints:
529
+ rowrhs[idx] = -lp._constraints[row].constant
530
+ rowsense[idx] = coptrsense[lp._constraints[row].sense]
531
531
  rowname[idx] = coptstr(row)
532
532
 
533
533
  self.c2n[row] = idx
@@ -946,7 +946,7 @@ class COPT(LpSolver):
946
946
  if not model.ismip:
947
947
  # NOTE: slacks in COPT are activities of rows
948
948
  slacks = model.getInfo("Slack", model.getConstrs())
949
- for constr, value in zip(lp.constraints.values(), slacks):
949
+ for constr, value in zip(lp._constraints.values(), slacks):
950
950
  constr.slack = value
951
951
 
952
952
  redcosts = model.getInfo("RedCost", model.getVars())
@@ -954,7 +954,7 @@ class COPT(LpSolver):
954
954
  var.dj = value
955
955
 
956
956
  duals = model.getInfo("Dual", model.getConstrs())
957
- for constr, value in zip(lp.constraints.values(), duals):
957
+ for constr, value in zip(lp._constraints.values(), duals):
958
958
  constr.pi = value
959
959
 
960
960
  return status
@@ -1013,7 +1013,7 @@ class COPT(LpSolver):
1013
1013
  self.coptmdl.setMipStart(var.solverVar, var.varValue)
1014
1014
  self.coptmdl.loadMipStart()
1015
1015
 
1016
- for name, constraint in lp.constraints.items():
1016
+ for name, constraint in lp._constraints.items():
1017
1017
  expr = coptpy.LinExpr(
1018
1018
  [v.solverVar for v in constraint.keys()], list(constraint.values())
1019
1019
  )
@@ -1042,7 +1042,7 @@ class COPT(LpSolver):
1042
1042
  solutionStatus = self.findSolutionValues(lp)
1043
1043
  for var in lp._variables:
1044
1044
  var.modified = False
1045
- for constraint in lp.constraints.values():
1045
+ for constraint in lp._constraints.values():
1046
1046
  constraint.modified = False
1047
1047
  return solutionStatus
1048
1048
 
@@ -1052,7 +1052,7 @@ class COPT(LpSolver):
1052
1052
 
1053
1053
  uses the old solver and modifies the rhs of the modified constraints
1054
1054
  """
1055
- for constraint in lp.constraints.values():
1055
+ for constraint in lp._constraints.values():
1056
1056
  if constraint.modified:
1057
1057
  if constraint.sense == LpConstraintLE:
1058
1058
  constraint.solverConstraint.ub = -constraint.constant
@@ -1067,6 +1067,6 @@ class COPT(LpSolver):
1067
1067
  solutionStatus = self.findSolutionValues(lp)
1068
1068
  for var in lp._variables:
1069
1069
  var.modified = False
1070
- for constraint in lp.constraints.values():
1070
+ for constraint in lp._constraints.values():
1071
1071
  constraint.modified = False
1072
1072
  return solutionStatus
@@ -200,7 +200,7 @@ class LpSolver:
200
200
  else:
201
201
  upperBounds[self.v2n[v]] = infBound
202
202
  # values for constraints
203
- numRows = len(lp.constraints)
203
+ numRows = len(lp._constraints)
204
204
  NumRowDoubleArray = ctypes.c_double * numRows
205
205
  NumRowStrArray = ctypes.c_char_p * numRows
206
206
  NumRowCharArray = ctypes.c_char * numRows
@@ -211,12 +211,12 @@ class LpSolver:
211
211
  self.c2n = {}
212
212
  self.n2c = {}
213
213
  i = 0
214
- for c in lp.constraints:
215
- rhsValues[i] = -lp.constraints[c].constant
214
+ for c in lp._constraints:
215
+ rhsValues[i] = -lp._constraints[c].constant
216
216
  # for ranged constraints a<= constraint >=b
217
217
  rangeValues[i] = 0.0
218
218
  rowNames[i] = to_string(c)
219
- rowType[i] = to_string(senseDict[lp.constraints[c].sense])
219
+ rowType[i] = to_string(senseDict[lp._constraints[c].sense])
220
220
  self.c2n[c] = i
221
221
  self.n2c[i] = c
222
222
  i = i + 1
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import warnings
3
- from typing import Iterable, Optional
3
+ from typing import Any, Iterable, Optional
4
4
 
5
5
  from .. import constants
6
6
  from .core import LpSolver, LpSolver_CMD, PulpSolverError, clock, log, subprocess
@@ -277,7 +277,11 @@ class CPLEX_PY(LpSolver):
277
277
  """True if the solver is available"""
278
278
  return False
279
279
 
280
- def actualSolve(self, lp):
280
+ def actualSolve(
281
+ self,
282
+ lp: Any,
283
+ callback: Optional[Iterable[type[Any]]] = None,
284
+ ) -> Any:
281
285
  """Solve a well formulated lp problem"""
282
286
  raise PulpSolverError(f"CPLEX_PY: Not Available:\n{self.err}")
283
287
 
@@ -330,7 +334,11 @@ class CPLEX_PY(LpSolver):
330
334
  """True if the solver is available"""
331
335
  return True
332
336
 
333
- def actualSolve(self, lp, callback: Optional[Iterable[type[cplex.callbacks.Callback]]] = None): # type: ignore[misc]
337
+ def actualSolve(
338
+ self,
339
+ lp: Any,
340
+ callback: Optional[Iterable[type[cplex.callbacks.Callback]]] = None,
341
+ ) -> Any:
334
342
  """
335
343
  Solve a well formulated lp problem
336
344
 
@@ -347,7 +355,7 @@ class CPLEX_PY(LpSolver):
347
355
  solutionStatus = self.findSolutionValues(lp)
348
356
  for var in lp._variables:
349
357
  var.modified = False
350
- for constraint in lp.constraints.values():
358
+ for constraint in lp._constraints.values():
351
359
  constraint.modified = False
352
360
  return solutionStatus
353
361
 
@@ -406,8 +414,8 @@ class CPLEX_PY(LpSolver):
406
414
  rows = []
407
415
  senses = []
408
416
  rhs = []
409
- rownames = list(lp.constraints.keys())
410
- for constraint in lp.constraints.values():
417
+ rownames = list(lp._constraints.keys())
418
+ for constraint in lp._constraints.values():
411
419
  # build the expression
412
420
  if len(constraint) == 0:
413
421
  # if the constraint is empty
@@ -583,7 +591,7 @@ class CPLEX_PY(LpSolver):
583
591
  sol_status = CplexSolStatus.get(lp.cplex_status)
584
592
  lp.assignStatus(status, sol_status)
585
593
  var_names = [var.name for var in lp._variables]
586
- con_names = [con for con in lp.constraints]
594
+ con_names = [con for con in lp._constraints]
587
595
  try:
588
596
  objectiveValue = lp.solverModel.solution.get_objective_value()
589
597
  variablevalues = dict(
@@ -135,7 +135,7 @@ class CUOPT(LpSolver):
135
135
  var.dj = value
136
136
 
137
137
  duals = solution.get_dual_solution()
138
- for constr, value in zip(lp.constraints.values(), duals):
138
+ for constr, value in zip(lp._constraints.values(), duals):
139
139
  constr.pi = value
140
140
 
141
141
  return status
@@ -214,7 +214,7 @@ class CUOPT(LpSolver):
214
214
  rhs, sense = [], []
215
215
  matrix_data, matrix_indices, matrix_indptr = [], [], [0]
216
216
 
217
- for name, constraint in lp.constraints.items():
217
+ for name, constraint in lp._constraints.items():
218
218
  row_coeffs = []
219
219
  matrix_data.extend(list(constraint.values()))
220
220
  matrix_indices.extend([var_dict[v.name] for v in constraint.keys()])
@@ -247,7 +247,7 @@ class CUOPT(LpSolver):
247
247
  solutionStatus = self.findSolutionValues(lp, solution)
248
248
  for var in lp._variables:
249
249
  var.modified = False
250
- for constraint in lp.constraints.values():
250
+ for constraint in lp._constraints.values():
251
251
  constraint.modified = False
252
252
  return solutionStatus
253
253
 
@@ -260,7 +260,7 @@ class CUOPT(LpSolver):
260
260
  rhs = lp.solverModel.get_constraint_bounds()
261
261
  sense = lp.solverModel.get_row_types()
262
262
 
263
- for i, name, constraint in enumerate(lp.constraints.items()):
263
+ for i, name, constraint in enumerate(lp._constraints.items()):
264
264
  if constraint.modified:
265
265
  sense[i] = sense_conv[constraint.sense]
266
266
  rhs[i] = -constraint.constant
@@ -274,6 +274,6 @@ class CUOPT(LpSolver):
274
274
  solutionStatus = self.findSolutionValues(lp)
275
275
  for var in lp._variables:
276
276
  var.modified = False
277
- for constraint in lp.constraints.values():
277
+ for constraint in lp._constraints.values():
278
278
  constraint.modified = False
279
279
  return solutionStatus
@@ -188,7 +188,6 @@ class GLPK_CMD(LpSolver_CMD):
188
188
 
189
189
  # now actually read column values
190
190
  with open(solFile) as f:
191
-
192
191
  values = []
193
192
  status2 = constants.LpStatusUndefined
194
193
  vpos = 2
@@ -291,7 +290,7 @@ class PYGLPK(LpSolver):
291
290
  var.varValue = glpk.glp_get_col_prim(prob, var.glpk_index)
292
291
  var.dj = glpk.glp_get_col_dual(prob, var.glpk_index)
293
292
  # put pi and slack variables against the constraints
294
- for constr in lp.constraints.values():
293
+ for constr in lp._constraints.values():
295
294
  if self.mip and self.hasMIPConstraints(lp.solverModel):
296
295
  row_val = glpk.glp_mip_row_val(prob, constr.glpk_index)
297
296
  else:
@@ -337,8 +336,8 @@ class PYGLPK(LpSolver):
337
336
  if lp.sense == constants.LpMaximize:
338
337
  glpk.glp_set_obj_dir(prob, glpk.GLP_MAX)
339
338
  log.debug("add the constraints to the problem")
340
- glpk.glp_add_rows(prob, len(list(lp.constraints.keys())))
341
- for i, v in enumerate(lp.constraints.items(), start=1):
339
+ glpk.glp_add_rows(prob, len(list(lp._constraints.keys())))
340
+ for i, v in enumerate(lp._constraints.items(), start=1):
342
341
  name, constraint = v
343
342
  glpk.glp_set_row_name(prob, i, name)
344
343
  if constraint.sense == constants.LpConstraintLE:
@@ -385,7 +384,7 @@ class PYGLPK(LpSolver):
385
384
  if value:
386
385
  glpk.glp_set_obj_coef(prob, var.glpk_index, value)
387
386
  log.debug("set the problem matrix")
388
- for constraint in lp.constraints.values():
387
+ for constraint in lp._constraints.values():
389
388
  l = len(list(constraint.items()))
390
389
  ind = glpk.intArray(l + 1)
391
390
  val = glpk.doubleArray(l + 1)
@@ -412,7 +411,7 @@ class PYGLPK(LpSolver):
412
411
  solutionStatus = self.findSolutionValues(lp)
413
412
  for var in lp.variables():
414
413
  var.modified = False
415
- for constraint in lp.constraints.values():
414
+ for constraint in lp._constraints.values():
416
415
  constraint.modified = False
417
416
  return solutionStatus
418
417
 
@@ -425,7 +424,7 @@ class PYGLPK(LpSolver):
425
424
  """
426
425
  prob = lp.solverModel
427
426
  log.debug("Resolve the Model using glpk")
428
- for constraint in lp.constraints.values():
427
+ for constraint in lp._constraints.values():
429
428
  i = constraint.glpk_index
430
429
  if constraint.modified:
431
430
  if constraint.sense == constants.LpConstraintLE:
@@ -451,6 +450,6 @@ class PYGLPK(LpSolver):
451
450
  solutionStatus = self.findSolutionValues(lp)
452
451
  for var in lp.variables():
453
452
  var.modified = False
454
- for constraint in lp.constraints.values():
453
+ for constraint in lp._constraints.values():
455
454
  constraint.modified = False
456
455
  return solutionStatus
@@ -215,7 +215,7 @@ class GUROBI(LpSolver):
215
215
  var.varValue = value
216
216
  # populate pulp constraints slack
217
217
  for constr, value in zip(
218
- lp.constraints.values(),
218
+ lp._constraints.values(),
219
219
  model.getAttr(GRB.Attr.Slack, model.getConstrs()),
220
220
  ):
221
221
  constr.slack = value
@@ -227,7 +227,7 @@ class GUROBI(LpSolver):
227
227
  var.dj = value
228
228
 
229
229
  for constr, value in zip(
230
- lp.constraints.values(),
230
+ lp._constraints.values(),
231
231
  model.getAttr(GRB.Attr.Pi, model.getConstrs()),
232
232
  ):
233
233
  constr.pi = value
@@ -316,7 +316,7 @@ class GUROBI(LpSolver):
316
316
 
317
317
  lp.solverModel.update()
318
318
  log.debug("add the Constraints to the problem")
319
- for name, constraint in lp.constraints.items():
319
+ for name, constraint in lp._constraints.items():
320
320
  # build the expression
321
321
  expr = gp.LinExpr(
322
322
  list(constraint.values()), [v.solverVar for v in constraint.keys()]
@@ -352,7 +352,7 @@ class GUROBI(LpSolver):
352
352
  solutionStatus = self.findSolutionValues(lp)
353
353
  for var in lp._variables:
354
354
  var.modified = False
355
- for constraint in lp.constraints.values():
355
+ for constraint in lp._constraints.values():
356
356
  constraint.modified = False
357
357
  return solutionStatus
358
358
 
@@ -363,7 +363,7 @@ class GUROBI(LpSolver):
363
363
  uses the old solver and modifies the rhs of the modified constraints
364
364
  """
365
365
  log.debug("Resolve the Model using gurobi")
366
- for constraint in lp.constraints.values():
366
+ for constraint in lp._constraints.values():
367
367
  if constraint.modified:
368
368
  constraint.solverConstraint.setAttr(
369
369
  gp.GRB.Attr.RHS, -constraint.constant
@@ -374,7 +374,7 @@ class GUROBI(LpSolver):
374
374
  solutionStatus = self.findSolutionValues(lp)
375
375
  for var in lp._variables:
376
376
  var.modified = False
377
- for constraint in lp.constraints.values():
377
+ for constraint in lp._constraints.values():
378
378
  constraint.modified = False
379
379
  return solutionStatus
380
380
 
@@ -175,9 +175,12 @@ class HiGHS_CMD(LpSolver_CMD):
175
175
  if len(model_line) > 0:
176
176
  model_status = " ".join(model_line[0][3:]) # Model status: ...
177
177
  else:
178
- # ILP
179
- model_line = [line for line in lines if "Status" in line][0]
180
- model_status = " ".join(model_line[1:])
178
+ # ILP / MIP logs (format varies; time-limited runs may omit status lines)
179
+ mip_status_lines = [line for line in lines if "Status" in line]
180
+ if mip_status_lines:
181
+ model_status = " ".join(mip_status_lines[0][1:])
182
+ else:
183
+ model_status = ""
181
184
  sol_line = [line for line in lines if line[:2] == ["Solution", "status"]]
182
185
  sol_line = sol_line[0] if len(sol_line) > 0 else ["Not solved"]
183
186
  sol_status = sol_line[-1]
@@ -382,7 +385,7 @@ class HiGHS(LpSolver):
382
385
  var.index, highspy.HighsVarType.kInteger
383
386
  )
384
387
 
385
- for i, constraint in enumerate(lp.constraints.values()):
388
+ for i, constraint in enumerate(lp._constraints.values()):
386
389
  non_zero_constraint_items = [
387
390
  (var.index, coefficient)
388
391
  for var, coefficient in constraint.items()
@@ -492,7 +495,7 @@ class HiGHS(LpSolver):
492
495
  var.varValue = col_values[var.index]
493
496
  var.dj = col_duals[var.index]
494
497
 
495
- constraints_list = list(lp.constraints.values())
498
+ constraints_list = list(lp._constraints.values())
496
499
  row_values = list(solution.row_value)
497
500
  row_duals = list(solution.row_dual)
498
501
  for constraint in constraints_list:
@@ -526,7 +529,7 @@ class HiGHS(LpSolver):
526
529
  for var in lp.variables():
527
530
  var.modified = False
528
531
 
529
- for constraint in lp.constraints.values():
532
+ for constraint in lp._constraints.values():
530
533
  constraint.modifier = False
531
534
 
532
535
  lp.assignStatus(status, sol_status)
@@ -115,7 +115,7 @@ class MOSEK(LpSolver):
115
115
 
116
116
  def buildSolverModel(self, lp, inf=1e20):
117
117
  """Translate the problem into a Mosek task object."""
118
- self.cons = lp.constraints
118
+ self.cons = lp._constraints
119
119
  self.numcons = len(self.cons)
120
120
  self.cons_dict = {}
121
121
  i = 0
@@ -236,8 +236,8 @@ class MOSEK(LpSolver):
236
236
  try:
237
237
  self.xc = [0.0] * self.numcons
238
238
  self.task.getxc(self.solution_type, self.xc)
239
- for con in lp.constraints:
240
- lp.constraints[con].slack = -(
239
+ for con in lp._constraints:
240
+ lp._constraints[con].slack = -(
241
241
  self.cons[con].constant + self.xc[self.cons_dict[con]]
242
242
  )
243
243
  except mosek.Error:
@@ -257,8 +257,8 @@ class MOSEK(LpSolver):
257
257
  try:
258
258
  self.y = [0.0] * self.numcons
259
259
  self.task.gety(self.solution_type, self.y)
260
- for con in lp.constraints:
261
- lp.constraints[con].pi = self.y[self.cons_dict[con]]
260
+ for con in lp._constraints:
261
+ lp._constraints[con].pi = self.y[self.cons_dict[con]]
262
262
  except mosek.Error:
263
263
  pass
264
264
 
@@ -306,7 +306,7 @@ class MOSEK(LpSolver):
306
306
  lp.assignStatus(self.solution_status_dict[self.solsta])
307
307
  for var in lp.variables():
308
308
  var.modified = False
309
- for con in lp.constraints.values():
309
+ for con in lp._constraints.values():
310
310
  con.modified = False
311
311
  return lp.status
312
312
 
@@ -340,6 +340,6 @@ class MOSEK(LpSolver):
340
340
  lp.assignStatus(self.solution_status_dict[self.solsta])
341
341
  for var in lp.variables():
342
342
  var.modified = False
343
- for con in lp.constraints.values():
343
+ for con in lp._constraints.values():
344
344
  con.modified = False
345
345
  return lp.status