bnbpy 0.1.0__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 (144) hide show
  1. bnbpy-0.1.0/MANIFEST.in +4 -0
  2. bnbpy-0.1.0/PKG-INFO +344 -0
  3. bnbpy-0.1.0/README.md +296 -0
  4. bnbpy-0.1.0/pyproject.toml +55 -0
  5. bnbpy-0.1.0/setup.cfg +71 -0
  6. bnbpy-0.1.0/setup.py +181 -0
  7. bnbpy-0.1.0/src/bnbprob/__init__.py +0 -0
  8. bnbpy-0.1.0/src/bnbprob/gcol/__init__.py +57 -0
  9. bnbpy-0.1.0/src/bnbprob/gcol/base.py +63 -0
  10. bnbpy-0.1.0/src/bnbprob/gcol/colgen.py +337 -0
  11. bnbpy-0.1.0/src/bnbprob/gcol/coloring.py +154 -0
  12. bnbpy-0.1.0/src/bnbprob/gcol/dataloader.py +58 -0
  13. bnbpy-0.1.0/src/bnbprob/gcol/indset.py +209 -0
  14. bnbpy-0.1.0/src/bnbprob/gcol/plot.py +304 -0
  15. bnbpy-0.1.0/src/bnbprob/gcol/setcover.py +144 -0
  16. bnbpy-0.1.0/src/bnbprob/machdeadline/__init__.py +13 -0
  17. bnbpy-0.1.0/src/bnbprob/machdeadline/bnb.py +17 -0
  18. bnbpy-0.1.0/src/bnbprob/machdeadline/instance.py +104 -0
  19. bnbpy-0.1.0/src/bnbprob/machdeadline/job.py +33 -0
  20. bnbpy-0.1.0/src/bnbprob/machdeadline/lagrangian.py +237 -0
  21. bnbpy-0.1.0/src/bnbprob/machdeadline/problem.py +152 -0
  22. bnbpy-0.1.0/src/bnbprob/machdeadline/smith.py +47 -0
  23. bnbpy-0.1.0/src/bnbprob/milpy/__init__.py +4 -0
  24. bnbpy-0.1.0/src/bnbprob/milpy/colgen.py +331 -0
  25. bnbpy-0.1.0/src/bnbprob/milpy/problem.py +241 -0
  26. bnbpy-0.1.0/src/bnbprob/milpy/utils.py +3 -0
  27. bnbpy-0.1.0/src/bnbprob/pafssp/__init__.py +21 -0
  28. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/.gitignore +1 -0
  29. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/__init__.pxd +2 -0
  30. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/environ.cpp +4586 -0
  31. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/environ.pxd +320 -0
  32. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/environ.pyx +2 -0
  33. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/iga.cpp +116 -0
  34. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/iga.hpp +38 -0
  35. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/intensify.cpp +25 -0
  36. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/intensify.hpp +18 -0
  37. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/job.cpp +118 -0
  38. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/job.hpp +66 -0
  39. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/job_times.cpp +16 -0
  40. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/job_times.hpp +27 -0
  41. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/local_search.cpp +99 -0
  42. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/local_search.hpp +35 -0
  43. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/mach_graph.hpp +100 -0
  44. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/neh.cpp +139 -0
  45. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/neh.hpp +28 -0
  46. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/permutation.cpp +293 -0
  47. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/permutation.hpp +366 -0
  48. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/quick_constructive.cpp +26 -0
  49. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/quick_constructive.hpp +16 -0
  50. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/randomized_heur.cpp +56 -0
  51. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/randomized_heur.hpp +17 -0
  52. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/sigma.cpp +69 -0
  53. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/sigma.hpp +110 -0
  54. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/single_mach.hpp +92 -0
  55. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/two_mach.cpp +86 -0
  56. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/two_mach.hpp +33 -0
  57. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/utils.cpp +50 -0
  58. bnbpy-0.1.0/src/bnbprob/pafssp/cpp/utils.hpp +19 -0
  59. bnbpy-0.1.0/src/bnbprob/pafssp/cython/.gitignore +1 -0
  60. bnbpy-0.1.0/src/bnbprob/pafssp/cython/__init__.cpp +4618 -0
  61. bnbpy-0.1.0/src/bnbprob/pafssp/cython/__init__.py +0 -0
  62. bnbpy-0.1.0/src/bnbprob/pafssp/cython/__init__.pyx +2 -0
  63. bnbpy-0.1.0/src/bnbprob/pafssp/cython/bnb.cpp +21665 -0
  64. bnbpy-0.1.0/src/bnbprob/pafssp/cython/bnb.pxd +31 -0
  65. bnbpy-0.1.0/src/bnbprob/pafssp/cython/bnb.pyi +166 -0
  66. bnbpy-0.1.0/src/bnbprob/pafssp/cython/bnb.pyx +220 -0
  67. bnbpy-0.1.0/src/bnbprob/pafssp/cython/problem.cpp +21953 -0
  68. bnbpy-0.1.0/src/bnbprob/pafssp/cython/problem.pxd +117 -0
  69. bnbpy-0.1.0/src/bnbprob/pafssp/cython/problem.pyi +394 -0
  70. bnbpy-0.1.0/src/bnbprob/pafssp/cython/problem.pyx +402 -0
  71. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pyjob.cpp +13531 -0
  72. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pyjob.pxd +32 -0
  73. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pyjob.pyi +9 -0
  74. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pyjob.pyx +160 -0
  75. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pysigma.cpp +14041 -0
  76. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pysigma.pxd +29 -0
  77. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pysigma.pyi +206 -0
  78. bnbpy-0.1.0/src/bnbprob/pafssp/cython/pysigma.pyx +191 -0
  79. bnbpy-0.1.0/src/bnbprob/pafssp/cython/utils.cpp +6351 -0
  80. bnbpy-0.1.0/src/bnbprob/pafssp/cython/utils.pxd +10 -0
  81. bnbpy-0.1.0/src/bnbprob/pafssp/cython/utils.pyx +82 -0
  82. bnbpy-0.1.0/src/bnbprob/pafssp/environ.py +21 -0
  83. bnbpy-0.1.0/src/bnbprob/pafssp/instances.py +120 -0
  84. bnbpy-0.1.0/src/bnbprob/pafssp/machinegraph.py +78 -0
  85. bnbpy-0.1.0/src/bnbprob/pafssp/mip/__init__.py +4 -0
  86. bnbpy-0.1.0/src/bnbprob/pafssp/mip/disjunctive.py +96 -0
  87. bnbpy-0.1.0/src/bnbprob/pafssp/mip/positional.py +88 -0
  88. bnbpy-0.1.0/src/bnbprob/pafssp/plot.py +177 -0
  89. bnbpy-0.1.0/src/bnbprob/py.typed +0 -0
  90. bnbpy-0.1.0/src/bnbpy/__init__.py +54 -0
  91. bnbpy-0.1.0/src/bnbpy/colgen.py +208 -0
  92. bnbpy-0.1.0/src/bnbpy/cython/.gitignore +1 -0
  93. bnbpy-0.1.0/src/bnbpy/cython/__init__.cpp +7246 -0
  94. bnbpy-0.1.0/src/bnbpy/cython/__init__.pxd +7 -0
  95. bnbpy-0.1.0/src/bnbpy/cython/__init__.pyi +1 -0
  96. bnbpy-0.1.0/src/bnbpy/cython/__init__.pyx +8 -0
  97. bnbpy-0.1.0/src/bnbpy/cython/counter.cpp +10119 -0
  98. bnbpy-0.1.0/src/bnbpy/cython/counter.pxd +15 -0
  99. bnbpy-0.1.0/src/bnbpy/cython/counter.pyi +8 -0
  100. bnbpy-0.1.0/src/bnbpy/cython/counter.pyx +13 -0
  101. bnbpy-0.1.0/src/bnbpy/cython/mod_queue.cpp +18419 -0
  102. bnbpy-0.1.0/src/bnbpy/cython/mod_queue.pxd +54 -0
  103. bnbpy-0.1.0/src/bnbpy/cython/mod_queue.pyi +43 -0
  104. bnbpy-0.1.0/src/bnbpy/cython/mod_queue.pyx +194 -0
  105. bnbpy-0.1.0/src/bnbpy/cython/node.cpp +14302 -0
  106. bnbpy-0.1.0/src/bnbpy/cython/node.pxd +56 -0
  107. bnbpy-0.1.0/src/bnbpy/cython/node.pyi +81 -0
  108. bnbpy-0.1.0/src/bnbpy/cython/node.pyx +172 -0
  109. bnbpy-0.1.0/src/bnbpy/cython/priqueue.cpp +18858 -0
  110. bnbpy-0.1.0/src/bnbpy/cython/priqueue.pxd +74 -0
  111. bnbpy-0.1.0/src/bnbpy/cython/priqueue.pyi +197 -0
  112. bnbpy-0.1.0/src/bnbpy/cython/priqueue.pyx +193 -0
  113. bnbpy-0.1.0/src/bnbpy/cython/problem.cpp +13413 -0
  114. bnbpy-0.1.0/src/bnbpy/cython/problem.pxd +41 -0
  115. bnbpy-0.1.0/src/bnbpy/cython/problem.pyi +193 -0
  116. bnbpy-0.1.0/src/bnbpy/cython/problem.pyx +154 -0
  117. bnbpy-0.1.0/src/bnbpy/cython/search.cpp +28754 -0
  118. bnbpy-0.1.0/src/bnbpy/cython/search.pxd +103 -0
  119. bnbpy-0.1.0/src/bnbpy/cython/search.pyi +345 -0
  120. bnbpy-0.1.0/src/bnbpy/cython/search.pyx +576 -0
  121. bnbpy-0.1.0/src/bnbpy/cython/solution.cpp +13221 -0
  122. bnbpy-0.1.0/src/bnbpy/cython/solution.pxd +34 -0
  123. bnbpy-0.1.0/src/bnbpy/cython/solution.pyi +35 -0
  124. bnbpy-0.1.0/src/bnbpy/cython/solution.pyx +76 -0
  125. bnbpy-0.1.0/src/bnbpy/cython/status.cpp +5377 -0
  126. bnbpy-0.1.0/src/bnbpy/cython/status.pxd +12 -0
  127. bnbpy-0.1.0/src/bnbpy/cython/status.pyi +11 -0
  128. bnbpy-0.1.0/src/bnbpy/cython/status.pyx +2 -0
  129. bnbpy-0.1.0/src/bnbpy/logger.py +38 -0
  130. bnbpy-0.1.0/src/bnbpy/parallel.py +44 -0
  131. bnbpy-0.1.0/src/bnbpy/plot.py +120 -0
  132. bnbpy-0.1.0/src/bnbpy/py.typed +0 -0
  133. bnbpy-0.1.0/src/bnbpy/pypure/__init__.py +0 -0
  134. bnbpy-0.1.0/src/bnbpy/pypure/counter.py +9 -0
  135. bnbpy-0.1.0/src/bnbpy/pypure/node.py +150 -0
  136. bnbpy-0.1.0/src/bnbpy/pypure/problem.py +101 -0
  137. bnbpy-0.1.0/src/bnbpy/pypure/search.py +541 -0
  138. bnbpy-0.1.0/src/bnbpy/pypure/solution.py +62 -0
  139. bnbpy-0.1.0/src/bnbpy/pypure/status.py +12 -0
  140. bnbpy-0.1.0/src/bnbpy.egg-info/PKG-INFO +344 -0
  141. bnbpy-0.1.0/src/bnbpy.egg-info/SOURCES.txt +171 -0
  142. bnbpy-0.1.0/src/bnbpy.egg-info/dependency_links.txt +1 -0
  143. bnbpy-0.1.0/src/bnbpy.egg-info/requires.txt +41 -0
  144. bnbpy-0.1.0/src/bnbpy.egg-info/top_level.txt +2 -0
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include pyproject.toml
3
+ include setup.cfg
4
+ recursive-include src *
bnbpy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,344 @@
1
+ Metadata-Version: 2.4
2
+ Name: bnbpy
3
+ Version: 0.1.0
4
+ Summary: A Python general Branch & Bound framework.
5
+ Home-page: https://github.com/bruscalia/bnbpy
6
+ Author: Bruno Scalia C. F. Leite
7
+ Author-email: bruscalia12@gmail.com
8
+ License: MIT
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: numpy>=1.21
11
+ Requires-Dist: scipy>=1.9
12
+ Requires-Dist: Cython==3.*
13
+ Requires-Dist: matplotlib==3.*
14
+ Requires-Dist: networkx==3.*
15
+ Requires-Dist: gif==23.*
16
+ Provides-Extra: lint
17
+ Requires-Dist: pytest==7.*; extra == "lint"
18
+ Requires-Dist: pytest-mock==3.14.*; extra == "lint"
19
+ Requires-Dist: pytest-cov==6.2.*; extra == "lint"
20
+ Requires-Dist: ruff==0.13.*; extra == "lint"
21
+ Requires-Dist: mypy==1.*; extra == "lint"
22
+ Requires-Dist: pydantic==2.*; extra == "lint"
23
+ Requires-Dist: pandas==2.*; extra == "lint"
24
+ Provides-Extra: tests
25
+ Requires-Dist: pytest==7.*; extra == "tests"
26
+ Requires-Dist: pytest-mock==3.14.*; extra == "tests"
27
+ Requires-Dist: pytest-cov==6.2.*; extra == "tests"
28
+ Requires-Dist: ruff==0.13.*; extra == "tests"
29
+ Requires-Dist: mypy==1.*; extra == "tests"
30
+ Requires-Dist: pydantic==2.*; extra == "tests"
31
+ Requires-Dist: pandas==2.*; extra == "tests"
32
+ Requires-Dist: pyomo==6.*; extra == "tests"
33
+ Requires-Dist: pandas[excel]==2.*; extra == "tests"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest==7.*; extra == "dev"
36
+ Requires-Dist: pytest-mock==3.14.*; extra == "dev"
37
+ Requires-Dist: pytest-cov==6.2.*; extra == "dev"
38
+ Requires-Dist: ruff==0.13.*; extra == "dev"
39
+ Requires-Dist: mypy==1.*; extra == "dev"
40
+ Requires-Dist: pydantic==2.*; extra == "dev"
41
+ Requires-Dist: pandas==2.*; extra == "dev"
42
+ Requires-Dist: pyomo==6.*; extra == "dev"
43
+ Requires-Dist: pandas[excel]==2.*; extra == "dev"
44
+ Requires-Dist: ipykernel; extra == "dev"
45
+ Requires-Dist: highspy==1.8.*; extra == "dev"
46
+ Requires-Dist: sphinx==9.*; extra == "dev"
47
+ Requires-Dist: sphinx-rtd-theme==3.*; extra == "dev"
48
+
49
+ # bnbpy
50
+
51
+
52
+ ![Tests](https://github.com/bruscalia/bnbpy/actions/workflows/test.yml/badge.svg)
53
+ ![Ruff](https://github.com/bruscalia/bnbpy/actions/workflows/ruff.yml/badge.svg)
54
+ ![Mypy](https://github.com/bruscalia/bnbpy/actions/workflows/mypy.yml/badge.svg)
55
+
56
+
57
+ A generic, configurable Python framework for solving optimization problems
58
+ using Branch & Bound. Also supports Column Generation and Branch & Price.
59
+
60
+ 📖 **[Documentation](https://bnbpy.readthedocs.io/en/latest/)**
61
+
62
+ [Configure](#configuration) | [Usage](#usage) | [Base Problems](#base-problems) | [Development](#development) | [Contact](#contact)
63
+
64
+ See more examples in the [notebooks folder](./notebooks/):
65
+
66
+ - [Single Machine Scheduling](./notebooks/single-machine.ipynb)
67
+ - [Permutation Flow Shop](./notebooks/pfssp.ipynb)
68
+ - [Permutation Assembly Flow Shop](./notebooks/pafssp.ipynb)
69
+ - [MILP](./notebooks/milp.ipynb)
70
+ - [Cutting Stock](./notebooks/cutting-stock.ipynb)
71
+ - [Graph Coloring](./notebooks/graph-coloring.ipynb)
72
+ - [Lot Sizing](./notebooks/lot-size.ipynb)
73
+
74
+ ## Configuration
75
+
76
+ ### Installation
77
+
78
+ #### PyPI
79
+
80
+ The `bnbpy` package is available in PyPI. So you can install it with a simple.
81
+
82
+ ```shell
83
+ pip install bnbpy
84
+ ```
85
+
86
+ #### Source Code
87
+
88
+ Alternatively, you can clone the repository and build it locally on your computer. Ensure you have a c++ compiler available due to Cython and c++ core components.
89
+
90
+ To clone the repository, you can run:
91
+
92
+ ```shell
93
+ git clone https://github.com/bruscalia/bnbpy.git
94
+ cd bnbpy
95
+ ```
96
+
97
+ Install the package and its dependencies via pip from the root of the repository:
98
+
99
+ ```
100
+ python -m pip install .
101
+ ```
102
+
103
+ To include development resources for running `bnbprob`, tests, and linters, use the `dev` extra:
104
+
105
+ ```
106
+ python -m pip install .[dev]
107
+ ```
108
+
109
+ For editable (development) mode, which allows you to track changes in the source files:
110
+
111
+ ```
112
+ python -m pip install -e .[dev]
113
+ ```
114
+
115
+ All package dependencies and metadata are specified in [setup.cfg](./setup.cfg). Build requirements and extension settings are in [pyproject.toml](./pyproject.toml).
116
+
117
+
118
+ ### Building
119
+
120
+ To build Cython extensions, run:
121
+
122
+ ```
123
+ python setup.py build_ext --inplace
124
+ ```
125
+
126
+ Be aware of deprecated commands in `setup.py`. See [here](https://packaging.python.org/en/latest/discussions/setup-py-deprecated/#needs-build) for more information.
127
+
128
+ To build wheels for distribution, run:
129
+
130
+ ```
131
+ python -m build --wheel
132
+ ```
133
+
134
+ ## Usage
135
+
136
+ ### Branch & Bound
137
+
138
+ ```python
139
+ from bnbpy import BranchAndBound, Problem
140
+
141
+ class MyProblem(Problem):
142
+ # Define your problem specifications by implementing abstract methods
143
+
144
+ def calc_bound(self):
145
+ # Compute node lower bound
146
+ pass
147
+
148
+ def is_feasible(self):
149
+ # Verify if node is feasible in the complete problem
150
+ pass
151
+
152
+ def branch(self):
153
+ # Return a list of subproblems if not fathomed
154
+ pass
155
+
156
+ # Apply Branch & Bound
157
+ problem = MyProblem()
158
+ bnb = BranchAndBound()
159
+ bnb.solve(problem)
160
+ print(bnb.solution)
161
+ ```
162
+
163
+ ### Branch & Price
164
+
165
+ ```python
166
+ import bnbpy as bbp
167
+
168
+ class MyMaster(bbp.Master):
169
+ def __init__(self, *args):
170
+ # Implement your master problem
171
+ pass
172
+
173
+ def add_col(self, c) -> bool:
174
+ # Include a new column and return True if accepted
175
+ return True
176
+
177
+ def solve(self) -> bbp.MasterSol:
178
+ # cost = ...
179
+ # duals = ...
180
+ # return MasterSol(cost=cost, duals=duals)
181
+ pass
182
+
183
+ class MyPricing(bbp.Pricing):
184
+ def __init__(self, **kwargs):
185
+ super().__init__(**kwargs)
186
+ # Instantiate your pricing problem
187
+
188
+ def set_weights(self, c):
189
+ # Modify the instance by incorporating new weights
190
+ pass
191
+
192
+ def solve(self) -> bbp.PriceSol:
193
+ # red_cost = ...
194
+ # new_col = ...
195
+ # return PriceSol(red_cost=red_cost, new_col=new_col)
196
+ pass
197
+
198
+ class MyCG(bbp.ColumnGenProblem):
199
+ def __init__(self, **kwargs):
200
+ master = MyMaster()
201
+ pricing = MyPricing(**kwargs)
202
+ super().__init__(
203
+ master=master,
204
+ pricing=pricing,
205
+ )
206
+
207
+ def branch(self) -> list['MyCG'] | None:
208
+ # Create children
209
+ pass
210
+
211
+ def is_feasible(self) -> bool:
212
+ # Check for feasibility in the complete formulation
213
+ pass
214
+ ```
215
+
216
+ ## Base Problems
217
+
218
+ The `bnbprob` package incorporates elements of `bnbpy` to solve classical examples.
219
+ It is installed alongside `bnbpy`.
220
+
221
+ See examples for [MILP](#milp), [Permutation Flow Shop](#flow-shop), and [Graph Coloring](#graph-coloring) problems below.
222
+
223
+ ### MILP
224
+
225
+ The MILP implementation is based on `scipy.optimize.linprog` for solving LPs.
226
+
227
+ ```python
228
+ import numpy as np
229
+
230
+ from bnbprob.milpy import MILP
231
+ from bnbpy import BranchAndBound
232
+
233
+ # Simple problem
234
+ c = np.array([-5.0, -4.0])
235
+
236
+ A_ub = np.array([
237
+ [2.0, 3.0],
238
+ [2.0, 1.0]
239
+ ])
240
+ b_ub = np.array([12.0, 6.0])
241
+
242
+ milp = MILP(c, A_ub=A_ub, b_ub=b_ub)
243
+ bfs = BranchAndBound()
244
+ sol = bfs.solve(milp)
245
+ print(f"Sol: {sol} | x: {sol.x}")
246
+ # >>> Sol: Status: OPTIMAL | Cost: -18.0 | LB: -18.0 | x: [2. 2.]
247
+ ```
248
+
249
+ ### Permutation Flow Shop
250
+
251
+ In this example, an instance from the literature with 20 jobs on 10 machines is solved.
252
+ It takes less than 0.1 second, which is an impressive performance even compared to the best commercial MILP solvers.
253
+
254
+ ```python
255
+ import json
256
+ from bnbprob.pafssp import CallbackBnB, PermFlowShop, plot_gantt
257
+
258
+ with open("./data/flow-shop/reC11.json", mode="r", encoding="utf8") as f:
259
+ p = json.load(f)
260
+
261
+ problem = PermFlowShop.from_p(p, constructive='neh')
262
+ bnb = CallbackBnB()
263
+
264
+ # The commercial solver Gurobi took 600s (timeout) to find the near-optimal
265
+ # solution 1432
266
+ sol = bnb.solve(
267
+ problem, maxiter=1000000, timelimit=600
268
+ )
269
+ print(sol)
270
+ # >>> Status: OPTIMAL | Cost: 1431 | LB: 1431
271
+
272
+ plot_gantt(sol.sequence, figsize=[8, 3])
273
+ ```
274
+
275
+ ![pfssp](./data/images/gantt.png)
276
+
277
+ ### Graph Coloring
278
+
279
+ To illustrate the usability of the Branch & Price feature, the implementation
280
+ of Graph Coloring outperforms commercial solvers with basic MILP modeling.
281
+
282
+ ```python
283
+ from bnbprob import gcol
284
+ from bnbpy import BranchAndBound
285
+
286
+ # Instances are available in the 'data' folder
287
+ instance = gcol.load_instance("./data/gcol/gcol_70_7.txt")
288
+
289
+ # Solve a 70-node instance to optimality in 24s
290
+ # With only heuristics to generate columns, it took only 14s
291
+ # The commercial solver Gurobi took 81s to find the optimal solution
292
+ # but could only prove optimality within 51 min
293
+ bnb = BranchAndBound()
294
+ price_tol = 0.1
295
+ pricing = gcol.ColorHybrPricing(
296
+ instance["edges"],
297
+ heur=gcol.TargetMultiStart(
298
+ 12, price_tol + 1, 20, 200
299
+ ),
300
+ price_tol=price_tol
301
+ )
302
+ problem = gcol.ColGenColor(
303
+ instance['edges'],
304
+ pricing=pricing,
305
+ max_iter_price=1000,
306
+ )
307
+ sol = bnb.solve(problem, maxiter=1000)
308
+ print(sol)
309
+ # >>> Status: OPTIMAL | Cost: 17 | LB: 17
310
+ ```
311
+
312
+ ## Development
313
+
314
+ ### Code Quality
315
+
316
+ This project uses [Ruff](https://docs.astral.sh/ruff/) as a linter and [Mypy](https://mypy-lang.org/) as a static type checker. Both are configured in [pyproject.toml](./pyproject.toml).
317
+
318
+ ```
319
+ mypy src/
320
+ ```
321
+
322
+ ```
323
+ ruff check src/
324
+ ```
325
+
326
+ ### Tests
327
+
328
+ Tests are configured in [pyproject.toml](./pyproject.toml). The package `pytest` is used as the main framework. To run tests, execute:
329
+
330
+ ```
331
+ pytest
332
+ ```
333
+
334
+ To track code coverage of the main package `bnbpy`, run:
335
+
336
+ ```
337
+ pytest --cov=bnbpy --cov-report=html
338
+ ```
339
+
340
+ Most of the package is already cythonized, so the coverage loses its meaning when raw.
341
+
342
+ ## Contact
343
+
344
+ Reach out to me at `bruscalia12@gmail.com` if you have any questions.
bnbpy-0.1.0/README.md ADDED
@@ -0,0 +1,296 @@
1
+ # bnbpy
2
+
3
+
4
+ ![Tests](https://github.com/bruscalia/bnbpy/actions/workflows/test.yml/badge.svg)
5
+ ![Ruff](https://github.com/bruscalia/bnbpy/actions/workflows/ruff.yml/badge.svg)
6
+ ![Mypy](https://github.com/bruscalia/bnbpy/actions/workflows/mypy.yml/badge.svg)
7
+
8
+
9
+ A generic, configurable Python framework for solving optimization problems
10
+ using Branch & Bound. Also supports Column Generation and Branch & Price.
11
+
12
+ 📖 **[Documentation](https://bnbpy.readthedocs.io/en/latest/)**
13
+
14
+ [Configure](#configuration) | [Usage](#usage) | [Base Problems](#base-problems) | [Development](#development) | [Contact](#contact)
15
+
16
+ See more examples in the [notebooks folder](./notebooks/):
17
+
18
+ - [Single Machine Scheduling](./notebooks/single-machine.ipynb)
19
+ - [Permutation Flow Shop](./notebooks/pfssp.ipynb)
20
+ - [Permutation Assembly Flow Shop](./notebooks/pafssp.ipynb)
21
+ - [MILP](./notebooks/milp.ipynb)
22
+ - [Cutting Stock](./notebooks/cutting-stock.ipynb)
23
+ - [Graph Coloring](./notebooks/graph-coloring.ipynb)
24
+ - [Lot Sizing](./notebooks/lot-size.ipynb)
25
+
26
+ ## Configuration
27
+
28
+ ### Installation
29
+
30
+ #### PyPI
31
+
32
+ The `bnbpy` package is available in PyPI. So you can install it with a simple.
33
+
34
+ ```shell
35
+ pip install bnbpy
36
+ ```
37
+
38
+ #### Source Code
39
+
40
+ Alternatively, you can clone the repository and build it locally on your computer. Ensure you have a c++ compiler available due to Cython and c++ core components.
41
+
42
+ To clone the repository, you can run:
43
+
44
+ ```shell
45
+ git clone https://github.com/bruscalia/bnbpy.git
46
+ cd bnbpy
47
+ ```
48
+
49
+ Install the package and its dependencies via pip from the root of the repository:
50
+
51
+ ```
52
+ python -m pip install .
53
+ ```
54
+
55
+ To include development resources for running `bnbprob`, tests, and linters, use the `dev` extra:
56
+
57
+ ```
58
+ python -m pip install .[dev]
59
+ ```
60
+
61
+ For editable (development) mode, which allows you to track changes in the source files:
62
+
63
+ ```
64
+ python -m pip install -e .[dev]
65
+ ```
66
+
67
+ All package dependencies and metadata are specified in [setup.cfg](./setup.cfg). Build requirements and extension settings are in [pyproject.toml](./pyproject.toml).
68
+
69
+
70
+ ### Building
71
+
72
+ To build Cython extensions, run:
73
+
74
+ ```
75
+ python setup.py build_ext --inplace
76
+ ```
77
+
78
+ Be aware of deprecated commands in `setup.py`. See [here](https://packaging.python.org/en/latest/discussions/setup-py-deprecated/#needs-build) for more information.
79
+
80
+ To build wheels for distribution, run:
81
+
82
+ ```
83
+ python -m build --wheel
84
+ ```
85
+
86
+ ## Usage
87
+
88
+ ### Branch & Bound
89
+
90
+ ```python
91
+ from bnbpy import BranchAndBound, Problem
92
+
93
+ class MyProblem(Problem):
94
+ # Define your problem specifications by implementing abstract methods
95
+
96
+ def calc_bound(self):
97
+ # Compute node lower bound
98
+ pass
99
+
100
+ def is_feasible(self):
101
+ # Verify if node is feasible in the complete problem
102
+ pass
103
+
104
+ def branch(self):
105
+ # Return a list of subproblems if not fathomed
106
+ pass
107
+
108
+ # Apply Branch & Bound
109
+ problem = MyProblem()
110
+ bnb = BranchAndBound()
111
+ bnb.solve(problem)
112
+ print(bnb.solution)
113
+ ```
114
+
115
+ ### Branch & Price
116
+
117
+ ```python
118
+ import bnbpy as bbp
119
+
120
+ class MyMaster(bbp.Master):
121
+ def __init__(self, *args):
122
+ # Implement your master problem
123
+ pass
124
+
125
+ def add_col(self, c) -> bool:
126
+ # Include a new column and return True if accepted
127
+ return True
128
+
129
+ def solve(self) -> bbp.MasterSol:
130
+ # cost = ...
131
+ # duals = ...
132
+ # return MasterSol(cost=cost, duals=duals)
133
+ pass
134
+
135
+ class MyPricing(bbp.Pricing):
136
+ def __init__(self, **kwargs):
137
+ super().__init__(**kwargs)
138
+ # Instantiate your pricing problem
139
+
140
+ def set_weights(self, c):
141
+ # Modify the instance by incorporating new weights
142
+ pass
143
+
144
+ def solve(self) -> bbp.PriceSol:
145
+ # red_cost = ...
146
+ # new_col = ...
147
+ # return PriceSol(red_cost=red_cost, new_col=new_col)
148
+ pass
149
+
150
+ class MyCG(bbp.ColumnGenProblem):
151
+ def __init__(self, **kwargs):
152
+ master = MyMaster()
153
+ pricing = MyPricing(**kwargs)
154
+ super().__init__(
155
+ master=master,
156
+ pricing=pricing,
157
+ )
158
+
159
+ def branch(self) -> list['MyCG'] | None:
160
+ # Create children
161
+ pass
162
+
163
+ def is_feasible(self) -> bool:
164
+ # Check for feasibility in the complete formulation
165
+ pass
166
+ ```
167
+
168
+ ## Base Problems
169
+
170
+ The `bnbprob` package incorporates elements of `bnbpy` to solve classical examples.
171
+ It is installed alongside `bnbpy`.
172
+
173
+ See examples for [MILP](#milp), [Permutation Flow Shop](#flow-shop), and [Graph Coloring](#graph-coloring) problems below.
174
+
175
+ ### MILP
176
+
177
+ The MILP implementation is based on `scipy.optimize.linprog` for solving LPs.
178
+
179
+ ```python
180
+ import numpy as np
181
+
182
+ from bnbprob.milpy import MILP
183
+ from bnbpy import BranchAndBound
184
+
185
+ # Simple problem
186
+ c = np.array([-5.0, -4.0])
187
+
188
+ A_ub = np.array([
189
+ [2.0, 3.0],
190
+ [2.0, 1.0]
191
+ ])
192
+ b_ub = np.array([12.0, 6.0])
193
+
194
+ milp = MILP(c, A_ub=A_ub, b_ub=b_ub)
195
+ bfs = BranchAndBound()
196
+ sol = bfs.solve(milp)
197
+ print(f"Sol: {sol} | x: {sol.x}")
198
+ # >>> Sol: Status: OPTIMAL | Cost: -18.0 | LB: -18.0 | x: [2. 2.]
199
+ ```
200
+
201
+ ### Permutation Flow Shop
202
+
203
+ In this example, an instance from the literature with 20 jobs on 10 machines is solved.
204
+ It takes less than 0.1 second, which is an impressive performance even compared to the best commercial MILP solvers.
205
+
206
+ ```python
207
+ import json
208
+ from bnbprob.pafssp import CallbackBnB, PermFlowShop, plot_gantt
209
+
210
+ with open("./data/flow-shop/reC11.json", mode="r", encoding="utf8") as f:
211
+ p = json.load(f)
212
+
213
+ problem = PermFlowShop.from_p(p, constructive='neh')
214
+ bnb = CallbackBnB()
215
+
216
+ # The commercial solver Gurobi took 600s (timeout) to find the near-optimal
217
+ # solution 1432
218
+ sol = bnb.solve(
219
+ problem, maxiter=1000000, timelimit=600
220
+ )
221
+ print(sol)
222
+ # >>> Status: OPTIMAL | Cost: 1431 | LB: 1431
223
+
224
+ plot_gantt(sol.sequence, figsize=[8, 3])
225
+ ```
226
+
227
+ ![pfssp](./data/images/gantt.png)
228
+
229
+ ### Graph Coloring
230
+
231
+ To illustrate the usability of the Branch & Price feature, the implementation
232
+ of Graph Coloring outperforms commercial solvers with basic MILP modeling.
233
+
234
+ ```python
235
+ from bnbprob import gcol
236
+ from bnbpy import BranchAndBound
237
+
238
+ # Instances are available in the 'data' folder
239
+ instance = gcol.load_instance("./data/gcol/gcol_70_7.txt")
240
+
241
+ # Solve a 70-node instance to optimality in 24s
242
+ # With only heuristics to generate columns, it took only 14s
243
+ # The commercial solver Gurobi took 81s to find the optimal solution
244
+ # but could only prove optimality within 51 min
245
+ bnb = BranchAndBound()
246
+ price_tol = 0.1
247
+ pricing = gcol.ColorHybrPricing(
248
+ instance["edges"],
249
+ heur=gcol.TargetMultiStart(
250
+ 12, price_tol + 1, 20, 200
251
+ ),
252
+ price_tol=price_tol
253
+ )
254
+ problem = gcol.ColGenColor(
255
+ instance['edges'],
256
+ pricing=pricing,
257
+ max_iter_price=1000,
258
+ )
259
+ sol = bnb.solve(problem, maxiter=1000)
260
+ print(sol)
261
+ # >>> Status: OPTIMAL | Cost: 17 | LB: 17
262
+ ```
263
+
264
+ ## Development
265
+
266
+ ### Code Quality
267
+
268
+ This project uses [Ruff](https://docs.astral.sh/ruff/) as a linter and [Mypy](https://mypy-lang.org/) as a static type checker. Both are configured in [pyproject.toml](./pyproject.toml).
269
+
270
+ ```
271
+ mypy src/
272
+ ```
273
+
274
+ ```
275
+ ruff check src/
276
+ ```
277
+
278
+ ### Tests
279
+
280
+ Tests are configured in [pyproject.toml](./pyproject.toml). The package `pytest` is used as the main framework. To run tests, execute:
281
+
282
+ ```
283
+ pytest
284
+ ```
285
+
286
+ To track code coverage of the main package `bnbpy`, run:
287
+
288
+ ```
289
+ pytest --cov=bnbpy --cov-report=html
290
+ ```
291
+
292
+ Most of the package is already cythonized, so the coverage loses its meaning when raw.
293
+
294
+ ## Contact
295
+
296
+ Reach out to me at `bruscalia12@gmail.com` if you have any questions.
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=64",
4
+ "wheel",
5
+ "Cython>=3.0",
6
+ "numpy>=1.19.0"
7
+ ]
8
+ build-backend = "setuptools.build_meta"
9
+
10
+ [tool.ruff]
11
+ line-length = 79
12
+
13
+ [tool.ruff.lint]
14
+ preview = true
15
+ select = [
16
+ "E", # pycodestyle - code style errors
17
+ "F", # pyflakes - code quality
18
+ "I", # isort - import order
19
+ "W", # pycodestyle - code style warnings
20
+ "PL", # pylint - code quality
21
+ "PT", # flake8-pytest-style - code style for pytest
22
+ "C", # Verifica a complexidade de código (inclui C901)
23
+ "B", # flake8-bugbear - verifica code smells
24
+ ]
25
+
26
+ [tool.ruff.format]
27
+ preview = true
28
+ quote-style = "single"
29
+
30
+ [tool.mypy]
31
+ python_version = "3.12"
32
+ ignore_missing_imports = true
33
+ strict = true
34
+ disable_error_code = ["unused-ignore"]
35
+
36
+ [tool.pytest.ini_options]
37
+ testpaths = ["tests"]
38
+ pythonpath = ["src"]
39
+ markers = [
40
+ "machdeadline: Mark test related to machine deadline scenarios",
41
+ "pafssp: Mark test related to PAFSSP scenarios",
42
+ "milp: Mark test related to MILP problems",
43
+ "milpnaive: Mark test related to naive MILP implementations",
44
+ "knapsack: Mark test related to knapsack problem implementations",
45
+ "solution: Mark test related to Solution class",
46
+ "problem: Mark test related to Problem class",
47
+ "node: Mark test related to Node class",
48
+ "search: Mark test related to B&B search module",
49
+ "priqueue: Mark test related to priority queue classes",
50
+ "colgen: Mark test related to Column Generation",
51
+ "gcol: Mark test related to the Graph Coloring module",
52
+ "searchlogger: Mark test related to the SearchLogger class",
53
+ "core: Mark test for core functionality (solution, problem, node, search, priqueue)",
54
+ "integration: Mark for integration tests (machdeadline, pfssp, milp, milpnaive, knapsack, gcol)"
55
+ ]