PuLP 3.2.2__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 (60) hide show
  1. pulp-3.3.1/LICENSE +22 -0
  2. {pulp-3.2.2 → pulp-3.3.1}/PKG-INFO +40 -13
  3. {pulp-3.2.2 → pulp-3.3.1}/PuLP.egg-info/PKG-INFO +40 -13
  4. {pulp-3.2.2 → pulp-3.3.1}/PuLP.egg-info/SOURCES.txt +1 -0
  5. pulp-3.3.1/PuLP.egg-info/requires.txt +35 -0
  6. {pulp-3.2.2 → pulp-3.3.1}/README.rst +20 -8
  7. {pulp-3.2.2 → pulp-3.3.1}/pulp/__init__.py +25 -1
  8. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/coin_api.py +11 -7
  9. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/copt_api.py +10 -10
  10. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/core.py +4 -4
  11. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/cplex_api.py +37 -10
  12. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/cuopt_api.py +9 -7
  13. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/glpk_api.py +7 -8
  14. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/gurobi_api.py +6 -6
  15. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/highs_api.py +9 -6
  16. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/mosek_api.py +7 -7
  17. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/scip_api.py +4 -4
  18. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/xpress_api.py +138 -40
  19. {pulp-3.2.2 → pulp-3.3.1}/pulp/constants.py +2 -1
  20. {pulp-3.2.2 → pulp-3.3.1}/pulp/mps_lp.py +8 -8
  21. {pulp-3.2.2 → pulp-3.3.1}/pulp/pulp.py +410 -74
  22. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/bin_packing_problem.py +2 -2
  23. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/test_gurobipy_env.py +4 -4
  24. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/test_lpdot.py +7 -5
  25. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/test_pulp.py +386 -302
  26. {pulp-3.2.2 → pulp-3.3.1}/pyproject.toml +22 -8
  27. pulp-3.2.2/PuLP.egg-info/requires.txt +0 -12
  28. {pulp-3.2.2 → pulp-3.3.1}/PuLP.egg-info/dependency_links.txt +0 -0
  29. {pulp-3.2.2 → pulp-3.3.1}/PuLP.egg-info/entry_points.txt +0 -0
  30. {pulp-3.2.2 → pulp-3.3.1}/PuLP.egg-info/top_level.txt +0 -0
  31. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/__init__.py +0 -0
  32. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/choco_api.py +0 -0
  33. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/mipcl_api.py +0 -0
  34. {pulp-3.2.2 → pulp-3.3.1}/pulp/apis/sas_api.py +0 -0
  35. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/__init__.py +0 -0
  36. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/arm64/__init__.py +0 -0
  37. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/arm64/cbc +0 -0
  38. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/arm64/coin-license.txt +0 -0
  39. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i32/__init__.py +0 -0
  40. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i32/cbc +0 -0
  41. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i32/coin-license.txt +0 -0
  42. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i64/__init__.py +0 -0
  43. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i64/cbc +0 -0
  44. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/linux/i64/coin-license.txt +0 -0
  45. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/osx/i64/__init__.py +0 -0
  46. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/osx/i64/cbc +0 -0
  47. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/osx/i64/coin-license.txt +0 -0
  48. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/win/i32/__init__.py +0 -0
  49. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/win/i32/cbc.exe +0 -0
  50. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/win/i32/coin-license.txt +0 -0
  51. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/win/i64/__init__.py +0 -0
  52. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/win/i64/cbc.exe +0 -0
  53. {pulp-3.2.2 → pulp-3.3.1}/pulp/solverdir/cbc/win/i64/coin-license.txt +0 -0
  54. {pulp-3.2.2 → pulp-3.3.1}/pulp/sparse.py +0 -0
  55. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/__init__.py +0 -0
  56. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/run_tests.py +0 -0
  57. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/test_examples.py +0 -0
  58. {pulp-3.2.2 → pulp-3.3.1}/pulp/tests/test_sparse.py +0 -0
  59. {pulp-3.2.2 → pulp-3.3.1}/pulp/utilities.py +0 -0
  60. {pulp-3.2.2 → pulp-3.3.1}/setup.cfg +0 -0
pulp-3.3.1/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2002-2005, Jean-Sebastien Roy
2
+ Modifications Copyright (c) 2007- Stuart Anthony Mitchell
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a
5
+ copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included
13
+ in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
@@ -1,31 +1,46 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PuLP
3
- Version: 3.2.2
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>
7
7
  Maintainer-email: Franco Peschiera <pchtsp@gmail.com>
8
- License: MIT
8
+ License-Expression: MIT
9
9
  Project-URL: source, https://github.com/coin-or/pulp
10
10
  Project-URL: download, https://github.com/coin-or/pulp/archive/master.zip
11
11
  Keywords: Optimization,Linear Programming,Operations Research
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Environment :: Console
14
14
  Classifier: Intended Audience :: Science/Research
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Natural Language :: English
17
16
  Classifier: Programming Language :: Python
18
17
  Classifier: Topic :: Scientific/Engineering :: Mathematics
19
18
  Requires-Python: >=3.9
20
19
  Description-Content-Type: text/x-rst
20
+ License-File: LICENSE
21
21
  Provides-Extra: open-py
22
- Requires-Dist: cylp; sys_platform != "win32" and 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"
27
26
  Requires-Dist: coptpy; extra == "public-py"
28
27
  Requires-Dist: xpress; extra == "public-py"
28
+ Requires-Dist: cplex; (sys_platform != "darwin" or python_full_version < "3.12") and extra == "public-py"
29
+ Provides-Extra: highs
30
+ Requires-Dist: highspy==1.13; extra == "highs"
31
+ Provides-Extra: scip
32
+ Requires-Dist: pyscipopt; extra == "scip"
33
+ Provides-Extra: gurobi
34
+ Requires-Dist: gurobipy; extra == "gurobi"
35
+ Provides-Extra: xpress
36
+ Requires-Dist: xpress; extra == "xpress"
37
+ Provides-Extra: copt
38
+ Requires-Dist: coptpy; extra == "copt"
39
+ Provides-Extra: cplex
40
+ Requires-Dist: cplex; (sys_platform != "darwin" or python_full_version < "3.12") and extra == "cplex"
41
+ Provides-Extra: mosek
42
+ Requires-Dist: mosek; extra == "mosek"
43
+ Dynamic: license-file
29
44
 
30
45
  pulp
31
46
  **************************
@@ -56,22 +71,34 @@ The easiest way to install PuLP is with ``pip``. If ``pip`` is available on your
56
71
 
57
72
  Otherwise follow the download instructions on the `PyPi page <https://pypi.python.org/pypi/PuLP>`_.
58
73
 
74
+ Installing solvers
75
+ ----------------------
59
76
 
60
- Quickstart
61
- ===============
77
+ PuLP can use a variety of solvers. The default solver is the COIN-OR CBC solver, which is included with PuLP. If you want to use other solvers, PuLP offers a quick way to install most solvers via their pypi package (some require a commercial license for running or for running large models)::
62
78
 
63
- Use ``LpVariable`` to create new variables. To create a variable x with 0 ≤ x ≤ 3::
79
+ python -m pip install pulp[gurobi]
80
+ python -m pip install pulp[cplex]
81
+ python -m pip install pulp[xpress]
82
+ python -m pip install pulp[scip]
83
+ python -m pip install pulp[highs]
84
+ python -m pip install pulp[copt]
85
+ python -m pip install pulp[mosek]
64
86
 
65
- from pulp import *
66
- x = LpVariable("x", 0, 3)
67
87
 
68
- To create a binary variable, y, with values either 0 or 1::
88
+ If you want to install all open source solvers (scip, highs, cylp), you can use the shortcut::
89
+ python -m pip install pulp[open_py]
69
90
 
70
- y = LpVariable("y", cat="Binary")
91
+ For more information on how to install solvers, see the `guide on configuring solvers <https://coin-or.github.io/pulp/guides/how_to_configure_solvers.html>`_.
71
92
 
72
- Use ``LpProblem`` to create new problems. Create a problem called "myProblem" like so::
93
+ Quickstart
94
+ ===============
95
+
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``::
73
97
 
98
+ from pulp import *
74
99
  prob = LpProblem("myProblem", LpMinimize)
100
+ x = prob.add_variable("x", 0, 3)
101
+ y = prob.add_variable("y", cat="Binary")
75
102
 
76
103
  Combine variables in order to create expressions and constraints, and then add them to the problem.::
77
104
 
@@ -1,31 +1,46 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PuLP
3
- Version: 3.2.2
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>
7
7
  Maintainer-email: Franco Peschiera <pchtsp@gmail.com>
8
- License: MIT
8
+ License-Expression: MIT
9
9
  Project-URL: source, https://github.com/coin-or/pulp
10
10
  Project-URL: download, https://github.com/coin-or/pulp/archive/master.zip
11
11
  Keywords: Optimization,Linear Programming,Operations Research
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Environment :: Console
14
14
  Classifier: Intended Audience :: Science/Research
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Natural Language :: English
17
16
  Classifier: Programming Language :: Python
18
17
  Classifier: Topic :: Scientific/Engineering :: Mathematics
19
18
  Requires-Python: >=3.9
20
19
  Description-Content-Type: text/x-rst
20
+ License-File: LICENSE
21
21
  Provides-Extra: open-py
22
- Requires-Dist: cylp; sys_platform != "win32" and 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"
27
26
  Requires-Dist: coptpy; extra == "public-py"
28
27
  Requires-Dist: xpress; extra == "public-py"
28
+ Requires-Dist: cplex; (sys_platform != "darwin" or python_full_version < "3.12") and extra == "public-py"
29
+ Provides-Extra: highs
30
+ Requires-Dist: highspy==1.13; extra == "highs"
31
+ Provides-Extra: scip
32
+ Requires-Dist: pyscipopt; extra == "scip"
33
+ Provides-Extra: gurobi
34
+ Requires-Dist: gurobipy; extra == "gurobi"
35
+ Provides-Extra: xpress
36
+ Requires-Dist: xpress; extra == "xpress"
37
+ Provides-Extra: copt
38
+ Requires-Dist: coptpy; extra == "copt"
39
+ Provides-Extra: cplex
40
+ Requires-Dist: cplex; (sys_platform != "darwin" or python_full_version < "3.12") and extra == "cplex"
41
+ Provides-Extra: mosek
42
+ Requires-Dist: mosek; extra == "mosek"
43
+ Dynamic: license-file
29
44
 
30
45
  pulp
31
46
  **************************
@@ -56,22 +71,34 @@ The easiest way to install PuLP is with ``pip``. If ``pip`` is available on your
56
71
 
57
72
  Otherwise follow the download instructions on the `PyPi page <https://pypi.python.org/pypi/PuLP>`_.
58
73
 
74
+ Installing solvers
75
+ ----------------------
59
76
 
60
- Quickstart
61
- ===============
77
+ PuLP can use a variety of solvers. The default solver is the COIN-OR CBC solver, which is included with PuLP. If you want to use other solvers, PuLP offers a quick way to install most solvers via their pypi package (some require a commercial license for running or for running large models)::
62
78
 
63
- Use ``LpVariable`` to create new variables. To create a variable x with 0 ≤ x ≤ 3::
79
+ python -m pip install pulp[gurobi]
80
+ python -m pip install pulp[cplex]
81
+ python -m pip install pulp[xpress]
82
+ python -m pip install pulp[scip]
83
+ python -m pip install pulp[highs]
84
+ python -m pip install pulp[copt]
85
+ python -m pip install pulp[mosek]
64
86
 
65
- from pulp import *
66
- x = LpVariable("x", 0, 3)
67
87
 
68
- To create a binary variable, y, with values either 0 or 1::
88
+ If you want to install all open source solvers (scip, highs, cylp), you can use the shortcut::
89
+ python -m pip install pulp[open_py]
69
90
 
70
- y = LpVariable("y", cat="Binary")
91
+ For more information on how to install solvers, see the `guide on configuring solvers <https://coin-or.github.io/pulp/guides/how_to_configure_solvers.html>`_.
71
92
 
72
- Use ``LpProblem`` to create new problems. Create a problem called "myProblem" like so::
93
+ Quickstart
94
+ ===============
95
+
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``::
73
97
 
98
+ from pulp import *
74
99
  prob = LpProblem("myProblem", LpMinimize)
100
+ x = prob.add_variable("x", 0, 3)
101
+ y = prob.add_variable("y", cat="Binary")
75
102
 
76
103
  Combine variables in order to create expressions and constraints, and then add them to the problem.::
77
104
 
@@ -1,3 +1,4 @@
1
+ LICENSE
1
2
  README.rst
2
3
  pyproject.toml
3
4
  PuLP.egg-info/PKG-INFO
@@ -0,0 +1,35 @@
1
+
2
+ [copt]
3
+ coptpy
4
+
5
+ [cplex]
6
+
7
+ [cplex:sys_platform != "darwin" or python_full_version < "3.12"]
8
+ cplex
9
+
10
+ [gurobi]
11
+ gurobipy
12
+
13
+ [highs]
14
+ highspy==1.13
15
+
16
+ [mosek]
17
+ mosek
18
+
19
+ [open_py]
20
+ highspy==1.13
21
+ pyscipopt
22
+
23
+ [public_py]
24
+ gurobipy
25
+ coptpy
26
+ xpress
27
+
28
+ [public_py:sys_platform != "darwin" or python_full_version < "3.12"]
29
+ cplex
30
+
31
+ [scip]
32
+ pyscipopt
33
+
34
+ [xpress]
35
+ xpress
@@ -27,22 +27,34 @@ The easiest way to install PuLP is with ``pip``. If ``pip`` is available on your
27
27
 
28
28
  Otherwise follow the download instructions on the `PyPi page <https://pypi.python.org/pypi/PuLP>`_.
29
29
 
30
+ Installing solvers
31
+ ----------------------
30
32
 
31
- Quickstart
32
- ===============
33
+ PuLP can use a variety of solvers. The default solver is the COIN-OR CBC solver, which is included with PuLP. If you want to use other solvers, PuLP offers a quick way to install most solvers via their pypi package (some require a commercial license for running or for running large models)::
33
34
 
34
- Use ``LpVariable`` to create new variables. To create a variable x with 0 ≤ x ≤ 3::
35
+ python -m pip install pulp[gurobi]
36
+ python -m pip install pulp[cplex]
37
+ python -m pip install pulp[xpress]
38
+ python -m pip install pulp[scip]
39
+ python -m pip install pulp[highs]
40
+ python -m pip install pulp[copt]
41
+ python -m pip install pulp[mosek]
35
42
 
36
- from pulp import *
37
- x = LpVariable("x", 0, 3)
38
43
 
39
- To create a binary variable, y, with values either 0 or 1::
44
+ If you want to install all open source solvers (scip, highs, cylp), you can use the shortcut::
45
+ python -m pip install pulp[open_py]
40
46
 
41
- y = LpVariable("y", cat="Binary")
47
+ For more information on how to install solvers, see the `guide on configuring solvers <https://coin-or.github.io/pulp/guides/how_to_configure_solvers.html>`_.
42
48
 
43
- Use ``LpProblem`` to create new problems. Create a problem called "myProblem" like so::
49
+ Quickstart
50
+ ===============
51
+
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``::
44
53
 
54
+ from pulp import *
45
55
  prob = LpProblem("myProblem", LpMinimize)
56
+ x = prob.add_variable("x", 0, 3)
57
+ y = prob.add_variable("y", cat="Binary")
46
58
 
47
59
  Combine variables in order to create expressions and constraints, and then add them to the problem.::
48
60
 
@@ -29,13 +29,37 @@ 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 *
35
60
  from .apis import *
36
61
  from .utilities import *
37
62
  from .constants import *
38
- from .tests import pulpTestAll
39
63
 
40
64
  __doc__ = pulp.__doc__ # type: ignore[name-defined]
41
65
  __version__ = VERSION
@@ -108,6 +108,10 @@ class COIN_CMD(LpSolver_CMD):
108
108
  :param str timeMode: "elapsed": count wall-time to timeLimit; "cpu": count cpu-time
109
109
  :param int maxNodes: max number of nodes during branching. Stops the solving when reached.
110
110
  """
111
+ if warmStart and not keepFiles and operating_system == "win":
112
+ warnings.warn(
113
+ "When using CBC on Windows, warmStart requires keepFiles=True."
114
+ )
111
115
 
112
116
  LpSolver_CMD.__init__(
113
117
  self,
@@ -163,7 +167,7 @@ class COIN_CMD(LpSolver_CMD):
163
167
  vs = lp.writeLP(tmpLp)
164
168
  # In the Lp we do not create new variable or constraint names:
165
169
  variablesNames = {v.name: v.name for v in vs}
166
- constraintsNames = {c: c for c in lp.constraints}
170
+ constraintsNames = {c: c for c in lp._constraints}
167
171
  cmds = " " + tmpLp + " "
168
172
  if self.optionsDict.get("warmStart", False):
169
173
  self.writesol(tmpMst, lp, vs, variablesNames, constraintsNames)
@@ -319,7 +323,7 @@ class COIN_CMD(LpSolver_CMD):
319
323
  returns status, values, reducedCosts, shadowPrices, slacks, sol_status
320
324
  """
321
325
  variablesNames = {v.name: v.name for v in vs}
322
- constraintsNames = {c: c for c in lp.constraints}
326
+ constraintsNames = {c: c for c in lp._constraints}
323
327
  return self.readsol_MPS(filename, lp, vs, variablesNames, constraintsNames)
324
328
 
325
329
  def get_status(self, filename):
@@ -754,7 +758,7 @@ class YAPOSIB(LpSolver):
754
758
  var.varValue = var.solverVar.solution
755
759
  var.dj = var.solverVar.reducedcost
756
760
  # put pi and slack variables against the constraints
757
- for constr in lp.constraints.values():
761
+ for constr in lp._constraints.values():
758
762
  constr.pi = constr.solverConstraint.dual
759
763
  constr.slack = -constr.constant - constr.solverConstraint.activity
760
764
  if self.msg:
@@ -813,7 +817,7 @@ class YAPOSIB(LpSolver):
813
817
  prob.obj[col.index] = lp.objective.get(var, 0.0)
814
818
  var.solverVar = col
815
819
  log.debug("add the Constraints to the problem")
816
- for name, constraint in lp.constraints.items():
820
+ for name, constraint in lp._constraints.items():
817
821
  row = prob.rows.add(
818
822
  yaposib.vec(
819
823
  [
@@ -849,7 +853,7 @@ class YAPOSIB(LpSolver):
849
853
  solutionStatus = self.findSolutionValues(lp)
850
854
  for var in lp.variables():
851
855
  var.modified = False
852
- for constraint in lp.constraints.values():
856
+ for constraint in lp._constraints.values():
853
857
  constraint.modified = False
854
858
  return solutionStatus
855
859
 
@@ -861,7 +865,7 @@ class YAPOSIB(LpSolver):
861
865
  constraints
862
866
  """
863
867
  log.debug("Resolve the model using yaposib")
864
- for constraint in lp.constraints.values():
868
+ for constraint in lp._constraints.values():
865
869
  row = constraint.solverConstraint
866
870
  if constraint.modified:
867
871
  if constraint.sense == constants.LpConstraintLE:
@@ -878,7 +882,7 @@ class YAPOSIB(LpSolver):
878
882
  solutionStatus = self.findSolutionValues(lp)
879
883
  for var in lp.variables():
880
884
  var.modified = False
881
- for constraint in lp.constraints.values():
885
+ for constraint in lp._constraints.values():
882
886
  constraint.modified = False
883
887
  return solutionStatus
884
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
@@ -240,6 +240,21 @@ class CPLEX_CMD(LpSolver_CMD):
240
240
  return True
241
241
 
242
242
 
243
+ def check_solverModel(func):
244
+ """
245
+ Decorator that checks if the solverModel is initialized.
246
+
247
+ :raises PulpSolverError: If the ``solverModel`` attribute is not initialized
248
+ """
249
+
250
+ def wrapper(self, *args, **kwargs):
251
+ if self.solverModel is None:
252
+ raise PulpSolverError("solverModel not initialized")
253
+ return func(self, *args, **kwargs)
254
+
255
+ return wrapper
256
+
257
+
243
258
  class CPLEX_PY(LpSolver):
244
259
  """
245
260
  The CPLEX LP/MIP solver (via a Python Binding)
@@ -262,7 +277,11 @@ class CPLEX_PY(LpSolver):
262
277
  """True if the solver is available"""
263
278
  return False
264
279
 
265
- def actualSolve(self, lp):
280
+ def actualSolve(
281
+ self,
282
+ lp: Any,
283
+ callback: Optional[Iterable[type[Any]]] = None,
284
+ ) -> Any:
266
285
  """Solve a well formulated lp problem"""
267
286
  raise PulpSolverError(f"CPLEX_PY: Not Available:\n{self.err}")
268
287
 
@@ -308,13 +327,18 @@ class CPLEX_PY(LpSolver):
308
327
  logPath=logPath,
309
328
  threads=threads,
310
329
  )
311
- self.solverParams = solverParams
330
+ self.solver_params = solverParams
331
+ self.solverModel = None
312
332
 
313
333
  def available(self):
314
334
  """True if the solver is available"""
315
335
  return True
316
336
 
317
- 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:
318
342
  """
319
343
  Solve a well formulated lp problem
320
344
 
@@ -331,7 +355,7 @@ class CPLEX_PY(LpSolver):
331
355
  solutionStatus = self.findSolutionValues(lp)
332
356
  for var in lp._variables:
333
357
  var.modified = False
334
- for constraint in lp.constraints.values():
358
+ for constraint in lp._constraints.values():
335
359
  constraint.modified = False
336
360
  return solutionStatus
337
361
 
@@ -390,8 +414,8 @@ class CPLEX_PY(LpSolver):
390
414
  rows = []
391
415
  senses = []
392
416
  rhs = []
393
- rownames = list(lp.constraints.keys())
394
- for constraint in lp.constraints.values():
417
+ rownames = list(lp._constraints.keys())
418
+ for constraint in lp._constraints.values():
395
419
  # build the expression
396
420
  if len(constraint) == 0:
397
421
  # if the constraint is empty
@@ -444,7 +468,7 @@ class CPLEX_PY(LpSolver):
444
468
  self.solverModel.MIP_starts.add(
445
469
  cplex.SparsePair(ind=ind, val=val), effort, "1"
446
470
  )
447
- for param, value in self.solverParams.items():
471
+ for param, value in self.solver_params.items():
448
472
  self.set_param(param, value)
449
473
 
450
474
  def set_param(self, name: str, value):
@@ -461,6 +485,7 @@ class CPLEX_PY(LpSolver):
461
485
  param = self.search_param(name=name)
462
486
  return param.get()
463
487
 
488
+ @check_solverModel
464
489
  def search_param(self, name: str):
465
490
  """
466
491
  Searches for a solver model parameter by its name and returns the corresponding attribute.
@@ -474,12 +499,14 @@ class CPLEX_PY(LpSolver):
474
499
  param = getattr(param, attr)
475
500
  return param
476
501
 
502
+ @check_solverModel
477
503
  def get_all_params(self):
478
504
  """
479
505
  Returns all parameters from the solver model.
480
506
  """
481
507
  return self.solverModel.parameters.get_all()
482
508
 
509
+ @check_solverModel
483
510
  def get_changed_params(self):
484
511
  """
485
512
  Returns the parameters that have been changed in the solver model.
@@ -515,7 +542,7 @@ class CPLEX_PY(LpSolver):
515
542
 
516
543
  def callSolver(
517
544
  self,
518
- isMIP,
545
+ isMIP: bool,
519
546
  callback: Optional[Iterable[type[cplex.callbacks.Callback]]] = None,
520
547
  ):
521
548
  """
@@ -564,7 +591,7 @@ class CPLEX_PY(LpSolver):
564
591
  sol_status = CplexSolStatus.get(lp.cplex_status)
565
592
  lp.assignStatus(status, sol_status)
566
593
  var_names = [var.name for var in lp._variables]
567
- con_names = [con for con in lp.constraints]
594
+ con_names = [con for con in lp._constraints]
568
595
  try:
569
596
  objectiveValue = lp.solverModel.solution.get_objective_value()
570
597
  variablevalues = dict(