it works?
This commit is contained in:
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python: Test",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/test.py",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
,ross,maverick.fritz.box,14.05.2020 23:29,file:///home/ross/.config/libreoffice/4;
|
||||||
+140
@@ -0,0 +1,140 @@
|
|||||||
|
Solver command line: ['/usr/bin/cbc', '-seconds', '3200', '-allow', '2200', '-printingOptions', 'all', '-import', '/tmp/tmpgzf8zbpg.pyomo.lp', '-stat=1', '-solve', '-solu', '/tmp/tmpgzf8zbpg.pyomo.soln']
|
||||||
|
|
||||||
|
Welcome to the CBC MILP Solver
|
||||||
|
Version: 2.9.9
|
||||||
|
Build Date: Aug 7 2019
|
||||||
|
|
||||||
|
command line - /usr/bin/cbc -seconds 3200 -allow 2200 -printingOptions all -import /tmp/tmpgzf8zbpg.pyomo.lp -stat=1 -solve -solu /tmp/tmpgzf8zbpg.pyomo.soln (default strategy 1)
|
||||||
|
seconds was changed from 1e+100 to 3200
|
||||||
|
allowableGap was changed from 1e-10 to 2200
|
||||||
|
Option for printingOptions changed from normal to all
|
||||||
|
Presolve 63617 (-74184) rows, 25986 (-83735) columns and 256502 (-851515) elements
|
||||||
|
Statistics for presolved model
|
||||||
|
Original problem has 109096 integers (108160 of which binary)
|
||||||
|
Presolved problem has 25896 integers (25688 of which binary)
|
||||||
|
==== 21788 zero objective 4 different
|
||||||
|
21788 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
2638 variables have objective of 1000
|
||||||
|
==== absolute objective values 4 different
|
||||||
|
21788 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
2638 variables have objective of 1000
|
||||||
|
==== for integers 21788 zero objective 4 different
|
||||||
|
21788 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
2548 variables have objective of 1000
|
||||||
|
==== for integers absolute objective values 4 different
|
||||||
|
21788 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
2548 variables have objective of 1000
|
||||||
|
===== end objective counts
|
||||||
|
|
||||||
|
|
||||||
|
Problem has 63617 rows, 25986 columns (4198 with objective) and 256502 elements
|
||||||
|
There are 1650 singletons with objective
|
||||||
|
Column breakdown:
|
||||||
|
298 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf,
|
||||||
|
0 of type lo->up, 0 of type free, 0 of type fixed,
|
||||||
|
0 of type -inf->0.0, 0 of type -inf->up, 25688 of type 0.0->1.0
|
||||||
|
Row breakdown:
|
||||||
|
2704 of type E 0.0, 676 of type E 1.0, 0 of type E -1.0,
|
||||||
|
279 of type E other, 0 of type G 0.0, 0 of type G 1.0,
|
||||||
|
418 of type G other, 5408 of type L 0.0, 49868 of type L 1.0,
|
||||||
|
1560 of type L other, 0 of type Range 0.0->1.0, 2704 of type Range other,
|
||||||
|
0 of type Free
|
||||||
|
Continuous objective value is 178488 - 30.16 seconds
|
||||||
|
Cgl0002I 75712 variables fixed
|
||||||
|
Cgl0003I 0 fixed, 208 tightened bounds, 37490 strengthened rows, 47518 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1438 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 5 strengthened rows, 0 substitutions
|
||||||
|
Cgl0004I processed model has 24041 rows, 16522 columns (16432 integer (16224 of which binary)) and 124218 elements
|
||||||
|
Cbc0038I Initial state - 1405 integers unsatisfied sum - 471.24
|
||||||
|
Cbc0038I Pass 1: (45.34 seconds) suminf. 304.35185 (762) obj. 223295 iterations 6320
|
||||||
|
Cbc0038I Pass 2: (45.54 seconds) suminf. 259.90250 (677) obj. 229665 iterations 1456
|
||||||
|
Cbc0038I Pass 3: (45.71 seconds) suminf. 237.42785 (612) obj. 231568 iterations 757
|
||||||
|
Cbc0038I Pass 4: (45.76 seconds) suminf. 234.36193 (605) obj. 231838 iterations 256
|
||||||
|
Cbc0038I Pass 5: (45.80 seconds) suminf. 233.19893 (597) obj. 232546 iterations 202
|
||||||
|
Cbc0038I Pass 6: (45.83 seconds) suminf. 232.04656 (590) obj. 233310 iterations 71
|
||||||
|
Cbc0038I Pass 7: (45.86 seconds) suminf. 231.54656 (589) obj. 233360 iterations 8
|
||||||
|
Cbc0038I Pass 8: (45.89 seconds) suminf. 231.31610 (590) obj. 233360 iterations 40
|
||||||
|
Cbc0038I Pass 9: (45.92 seconds) suminf. 229.14180 (587) obj. 233352 iterations 22
|
||||||
|
Cbc0038I Pass 10: (45.94 seconds) suminf. 229.14180 (587) obj. 233352 iterations 1
|
||||||
|
Cbc0038I Pass 11: (45.98 seconds) suminf. 219.44422 (557) obj. 231301 iterations 113
|
||||||
|
Cbc0038I Pass 12: (46.02 seconds) suminf. 212.43029 (535) obj. 229437 iterations 116
|
||||||
|
Cbc0038I Pass 13: (46.03 seconds) suminf. 212.43029 (535) obj. 229437 iterations 0
|
||||||
|
Cbc0038I Pass 14: (46.06 seconds) suminf. 208.43029 (527) obj. 229837 iterations 31
|
||||||
|
Cbc0038I Pass 15: (46.08 seconds) suminf. 208.43029 (527) obj. 229837 iterations 1
|
||||||
|
Cbc0038I Pass 16: (46.11 seconds) suminf. 204.43029 (519) obj. 230237 iterations 18
|
||||||
|
Cbc0038I Pass 17: (46.14 seconds) suminf. 204.43029 (519) obj. 230237 iterations 3
|
||||||
|
Cbc0038I Pass 18: (46.17 seconds) suminf. 196.93029 (504) obj. 230987 iterations 35
|
||||||
|
Cbc0038I Pass 19: (46.19 seconds) suminf. 196.93029 (504) obj. 230987 iterations 2
|
||||||
|
Cbc0038I Pass 20: (46.22 seconds) suminf. 189.43029 (489) obj. 231737 iterations 25
|
||||||
|
Cbc0038I Pass 21: (46.25 seconds) suminf. 189.43029 (489) obj. 231737 iterations 6
|
||||||
|
Cbc0038I Pass 22: (46.27 seconds) suminf. 184.43029 (479) obj. 232237 iterations 25
|
||||||
|
Cbc0038I Pass 23: (46.30 seconds) suminf. 184.43029 (479) obj. 232237 iterations 4
|
||||||
|
Cbc0038I Pass 24: (46.33 seconds) suminf. 178.43029 (467) obj. 232837 iterations 25
|
||||||
|
Cbc0038I Pass 25: (46.36 seconds) suminf. 178.43029 (467) obj. 232837 iterations 3
|
||||||
|
Cbc0038I Pass 26: (46.38 seconds) suminf. 175.93029 (462) obj. 233087 iterations 16
|
||||||
|
Cbc0038I Pass 27: (46.41 seconds) suminf. 175.93029 (462) obj. 233087 iterations 5
|
||||||
|
Cbc0038I Pass 28: (46.44 seconds) suminf. 174.93029 (460) obj. 233187 iterations 7
|
||||||
|
Cbc0038I Pass 29: (46.46 seconds) suminf. 174.93029 (460) obj. 233187 iterations 1
|
||||||
|
Cbc0038I Pass 30: (46.49 seconds) suminf. 173.43029 (457) obj. 233337 iterations 6
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 14611 integers at bound fixed and 43 continuous
|
||||||
|
Cbc0038I Mini branch and bound did not improve solution (46.52 seconds)
|
||||||
|
Cbc0038I After 46.52 seconds - Feasibility pump exiting - took 3.36 seconds
|
||||||
|
Cbc0031I 552 added rows had average density of 29.197464
|
||||||
|
Cbc0013I At root node, 552 cuts changed objective from 188237.94 to 222332.75 in 10 passes
|
||||||
|
Cbc0014I Cut generator 0 (Probing) - 3440 row cuts average 3.4 elements, 0 column cuts (0 active) in 3.913 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 1 (Gomory) - 2373 row cuts average 245.7 elements, 0 column cuts (0 active) in 7.187 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 2 (Knapsack) - 1879 row cuts average 2.3 elements, 0 column cuts (0 active) in 0.268 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.095 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 1177 row cuts average 13.5 elements, 0 column cuts (0 active) in 0.242 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.039 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 6 (TwoMirCuts) - 3221 row cuts average 43.5 elements, 0 column cuts (0 active) in 4.283 seconds - new frequency is 1
|
||||||
|
Cbc0010I After 0 nodes, 1 on tree, 1e+50 best solution, best possible 222332.75 (95.56 seconds)
|
||||||
|
Cbc0010I After 100 nodes, 58 on tree, 1e+50 best solution, best possible 222332.75 (140.65 seconds)
|
||||||
|
Cbc0010I After 200 nodes, 109 on tree, 1e+50 best solution, best possible 222332.75 (153.72 seconds)
|
||||||
|
Cbc0010I After 300 nodes, 159 on tree, 1e+50 best solution, best possible 222332.75 (166.84 seconds)
|
||||||
|
Cbc0010I After 400 nodes, 211 on tree, 1e+50 best solution, best possible 222332.75 (180.19 seconds)
|
||||||
|
Cbc0010I After 500 nodes, 260 on tree, 1e+50 best solution, best possible 222332.75 (194.37 seconds)
|
||||||
|
Cbc0010I After 600 nodes, 312 on tree, 1e+50 best solution, best possible 222332.75 (208.13 seconds)
|
||||||
|
Cbc0010I After 700 nodes, 363 on tree, 1e+50 best solution, best possible 222332.75 (223.59 seconds)
|
||||||
|
Cbc0010I After 800 nodes, 412 on tree, 1e+50 best solution, best possible 222332.75 (239.21 seconds)
|
||||||
|
Cbc0012I Integer solution of 225548.92 found by rounding after 83817 iterations and 877 nodes (249.96 seconds)
|
||||||
|
Cbc0038I Full problem 24041 rows 16522 columns, reduced to 0 rows 0 columns
|
||||||
|
Cbc0038I Full problem 24041 rows 16522 columns, reduced to 716 rows 543 columns
|
||||||
|
Cbc0012I Integer solution of 224493.06 found by RINS after 85051 iterations and 900 nodes (257.47 seconds)
|
||||||
|
Cbc0010I After 900 nodes, 38 on tree, 224493.06 best solution, best possible 222332.75 (257.55 seconds)
|
||||||
|
Cbc0011I Exiting as integer gap of 2160.3118 less than 2200 or 0%%
|
||||||
|
Cbc0001I Search completed - best objective 224493.0622792392, took 85147 iterations and 901 nodes (257.69 seconds)
|
||||||
|
Cbc0032I Strong branching done 3076 times (85272 iterations), fathomed 0 nodes and fixed 0 variables
|
||||||
|
Cbc0035I Maximum depth 178, 1114 variables fixed on reduced cost
|
||||||
|
Cuts at root node changed objective from 188238 to 222333
|
||||||
|
Probing was tried 920 times and created 13923 cuts of which 0 were active after adding rounds of cuts (10.780 seconds)
|
||||||
|
Gomory was tried 920 times and created 4524 cuts of which 0 were active after adding rounds of cuts (24.789 seconds)
|
||||||
|
Knapsack was tried 920 times and created 9817 cuts of which 0 were active after adding rounds of cuts (12.716 seconds)
|
||||||
|
Clique was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.095 seconds)
|
||||||
|
MixedIntegerRounding2 was tried 920 times and created 5513 cuts of which 0 were active after adding rounds of cuts (16.556 seconds)
|
||||||
|
FlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.039 seconds)
|
||||||
|
TwoMirCuts was tried 920 times and created 9162 cuts of which 0 were active after adding rounds of cuts (11.763 seconds)
|
||||||
|
ImplicationCuts was tried 20 times and created 2198 cuts of which 0 were active after adding rounds of cuts (0.049 seconds)
|
||||||
|
|
||||||
|
Result - Optimal solution found (within gap tolerance)
|
||||||
|
|
||||||
|
Objective value: 224493.06227924
|
||||||
|
Lower bound: 222332.750
|
||||||
|
Gap: 0.01
|
||||||
|
Enumerated nodes: 901
|
||||||
|
Total iterations: 85147
|
||||||
|
Time (CPU seconds): 258.90
|
||||||
|
Time (Wallclock seconds): 259.64
|
||||||
|
|
||||||
|
Total time (CPU seconds): 261.09 (Wallclock seconds): 262.01
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
Solver command line: ['/usr/bin/cbc', '-seconds', '3200', '-allow', '2200', '-printingOptions', 'all', '-import', '/tmp/tmpm0xkmuez.pyomo.lp', '-stat=1', '-solve', '-solu', '/tmp/tmpm0xkmuez.pyomo.soln']
|
||||||
|
|
||||||
|
Welcome to the CBC MILP Solver
|
||||||
|
Version: 2.9.9
|
||||||
|
Build Date: Aug 7 2019
|
||||||
|
|
||||||
|
command line - /usr/bin/cbc -seconds 3200 -allow 2200 -printingOptions all -import /tmp/tmpm0xkmuez.pyomo.lp -stat=1 -solve -solu /tmp/tmpm0xkmuez.pyomo.soln (default strategy 1)
|
||||||
|
seconds was changed from 1e+100 to 3200
|
||||||
|
allowableGap was changed from 1e-10 to 2200
|
||||||
|
Option for printingOptions changed from normal to all
|
||||||
|
Presolve 19601 (-110296) rows, 15178 (-12903) columns and 97892 (-209117) elements
|
||||||
|
Statistics for presolved model
|
||||||
|
Original problem has 27872 integers (27040 of which binary)
|
||||||
|
Presolved problem has 15088 integers (14872 of which binary)
|
||||||
|
==== 13606 zero objective 3 different
|
||||||
|
13606 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1364 variables have objective of 1000
|
||||||
|
==== absolute objective values 3 different
|
||||||
|
13606 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1364 variables have objective of 1000
|
||||||
|
==== for integers 13606 zero objective 3 different
|
||||||
|
13606 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1274 variables have objective of 1000
|
||||||
|
==== for integers absolute objective values 3 different
|
||||||
|
13606 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1274 variables have objective of 1000
|
||||||
|
===== end objective counts
|
||||||
|
|
||||||
|
|
||||||
|
Problem has 19601 rows, 15178 columns (1572 with objective) and 97892 elements
|
||||||
|
There are 298 singletons with objective
|
||||||
|
Column breakdown:
|
||||||
|
306 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf,
|
||||||
|
0 of type lo->up, 0 of type free, 0 of type fixed,
|
||||||
|
0 of type -inf->0.0, 0 of type -inf->up, 14872 of type 0.0->1.0
|
||||||
|
Row breakdown:
|
||||||
|
2712 of type E 0.0, 0 of type E 1.0, 0 of type E -1.0,
|
||||||
|
227 of type E other, 0 of type G 0.0, 0 of type G 1.0,
|
||||||
|
334 of type G other, 4056 of type L 0.0, 10816 of type L 1.0,
|
||||||
|
1456 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other,
|
||||||
|
0 of type Free
|
||||||
|
Continuous objective value is 307977 - 1.79 seconds
|
||||||
|
Cgl0002I 9464 variables fixed
|
||||||
|
Cgl0003I 0 fixed, 208 tightened bounds, 12068 strengthened rows, 72112 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 3874 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 2414 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 913 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 472 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 426 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 419 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 416 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 394 strengthened rows, 0 substitutions
|
||||||
|
Cgl0004I processed model has 3557 rows, 3002 columns (2912 integer (2704 of which binary)) and 27294 elements
|
||||||
|
Cbc0038I Initial state - 203 integers unsatisfied sum - 48.1942
|
||||||
|
Cbc0038I Pass 1: (4.31 seconds) suminf. 25.79319 (98) obj. 308847 iterations 1030
|
||||||
|
Cbc0038I Pass 2: (4.31 seconds) suminf. 20.95041 (81) obj. 308850 iterations 75
|
||||||
|
Cbc0038I Pass 3: (4.32 seconds) suminf. 19.51710 (76) obj. 308846 iterations 18
|
||||||
|
Cbc0038I Pass 4: (4.32 seconds) suminf. 16.38158 (66) obj. 309105 iterations 146
|
||||||
|
Cbc0038I Pass 5: (4.33 seconds) suminf. 15.78139 (64) obj. 309104 iterations 56
|
||||||
|
Cbc0038I Pass 6: (4.34 seconds) suminf. 17.82947 (66) obj. 309861 iterations 149
|
||||||
|
Cbc0038I Pass 7: (4.34 seconds) suminf. 16.87253 (64) obj. 309853 iterations 56
|
||||||
|
Cbc0038I Pass 8: (4.35 seconds) suminf. 16.34740 (64) obj. 309851 iterations 17
|
||||||
|
Cbc0038I Pass 9: (4.36 seconds) suminf. 17.31553 (66) obj. 311385 iterations 128
|
||||||
|
Cbc0038I Pass 10: (4.36 seconds) suminf. 16.18974 (65) obj. 311381 iterations 52
|
||||||
|
Cbc0038I Pass 11: (4.37 seconds) suminf. 16.66807 (65) obj. 309850 iterations 85
|
||||||
|
Cbc0038I Pass 12: (4.37 seconds) suminf. 16.39342 (65) obj. 309855 iterations 36
|
||||||
|
Cbc0038I Pass 13: (4.38 seconds) suminf. 19.03518 (68) obj. 311416 iterations 132
|
||||||
|
Cbc0038I Pass 14: (4.39 seconds) suminf. 15.97431 (63) obj. 311425 iterations 75
|
||||||
|
Cbc0038I Pass 15: (4.39 seconds) suminf. 15.48765 (62) obj. 311431 iterations 18
|
||||||
|
Cbc0038I Pass 16: (4.40 seconds) suminf. 17.17728 (68) obj. 309925 iterations 172
|
||||||
|
Cbc0038I Pass 17: (4.41 seconds) suminf. 15.30830 (62) obj. 309922 iterations 83
|
||||||
|
Cbc0038I Pass 18: (4.42 seconds) suminf. 19.16642 (70) obj. 309215 iterations 152
|
||||||
|
Cbc0038I Pass 19: (4.42 seconds) suminf. 17.03452 (65) obj. 309214 iterations 68
|
||||||
|
Cbc0038I Pass 20: (4.43 seconds) suminf. 15.92705 (65) obj. 309214 iterations 29
|
||||||
|
Cbc0038I Pass 21: (4.43 seconds) suminf. 14.93939 (66) obj. 309217 iterations 15
|
||||||
|
Cbc0038I Pass 22: (4.44 seconds) suminf. 17.10473 (66) obj. 309204 iterations 185
|
||||||
|
Cbc0038I Pass 23: (4.45 seconds) suminf. 16.32949 (63) obj. 309208 iterations 28
|
||||||
|
Cbc0038I Pass 24: (4.46 seconds) suminf. 14.92720 (62) obj. 309202 iterations 70
|
||||||
|
Cbc0038I Pass 25: (4.46 seconds) suminf. 14.92720 (62) obj. 309202 iterations 12
|
||||||
|
Cbc0038I Pass 26: (4.47 seconds) suminf. 16.38608 (65) obj. 309206 iterations 77
|
||||||
|
Cbc0038I Pass 27: (4.47 seconds) suminf. 15.38263 (62) obj. 309211 iterations 26
|
||||||
|
Cbc0038I Pass 28: (4.48 seconds) suminf. 15.54781 (63) obj. 309199 iterations 61
|
||||||
|
Cbc0038I Pass 29: (4.51 seconds) suminf. 42.72391 (154) obj. 344500 iterations 566
|
||||||
|
Cbc0038I Pass 30: (4.52 seconds) suminf. 25.91427 (90) obj. 331553 iterations 359
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 2368 integers at bound fixed and 42 continuous
|
||||||
|
Cbc0038I Full problem 3557 rows 3002 columns, reduced to 819 rows 561 columns
|
||||||
|
Cbc0038I Mini branch and bound did not improve solution (5.26 seconds)
|
||||||
|
Cbc0038I After 5.26 seconds - Feasibility pump exiting - took 1.06 seconds
|
||||||
|
Cbc0031I 66 added rows had average density of 160.28788
|
||||||
|
Cbc0013I At root node, 66 cuts changed objective from 307976.81 to 308016.81 in 10 passes
|
||||||
|
Cbc0014I Cut generator 0 (Probing) - 274 row cuts average 19.0 elements, 0 column cuts (0 active) in 0.293 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 1 (Gomory) - 144 row cuts average 720.2 elements, 0 column cuts (0 active) in 0.649 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 2 (Knapsack) - 35 row cuts average 8.6 elements, 0 column cuts (0 active) in 0.023 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.005 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 81 row cuts average 42.5 elements, 0 column cuts (0 active) in 0.060 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.008 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 6 (TwoMirCuts) - 148 row cuts average 62.1 elements, 0 column cuts (0 active) in 0.153 seconds - new frequency is -100
|
||||||
|
Cbc0010I After 0 nodes, 1 on tree, 1e+50 best solution, best possible 308016.81 (7.77 seconds)
|
||||||
|
Cbc0012I Integer solution of 308036.81 found by DiveCoefficient after 2997 iterations and 2 nodes (8.23 seconds)
|
||||||
|
Cbc0038I Full problem 3557 rows 3002 columns, reduced to 89 rows 63 columns
|
||||||
|
Cbc0004I Integer solution of 308016.81 found after 3022 iterations and 3 nodes (8.83 seconds)
|
||||||
|
Cbc0011I Exiting as integer gap of -5.8207661e-11 less than 2200 or 0%%
|
||||||
|
Cbc0001I Search completed - best objective 308016.8141544065, took 3022 iterations and 3 nodes (8.83 seconds)
|
||||||
|
Cbc0032I Strong branching done 66 times (4711 iterations), fathomed 0 nodes and fixed 0 variables
|
||||||
|
Cbc0035I Maximum depth 2, 364 variables fixed on reduced cost
|
||||||
|
Cuts at root node changed objective from 307977 to 308017
|
||||||
|
Probing was tried 16 times and created 281 cuts of which 0 were active after adding rounds of cuts (0.305 seconds)
|
||||||
|
Gomory was tried 16 times and created 145 cuts of which 0 were active after adding rounds of cuts (0.669 seconds)
|
||||||
|
Knapsack was tried 16 times and created 35 cuts of which 0 were active after adding rounds of cuts (0.034 seconds)
|
||||||
|
Clique was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.005 seconds)
|
||||||
|
MixedIntegerRounding2 was tried 16 times and created 87 cuts of which 0 were active after adding rounds of cuts (0.089 seconds)
|
||||||
|
FlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.008 seconds)
|
||||||
|
TwoMirCuts was tried 10 times and created 148 cuts of which 0 were active after adding rounds of cuts (0.153 seconds)
|
||||||
|
ImplicationCuts was tried 6 times and created 2 cuts of which 0 were active after adding rounds of cuts (0.001 seconds)
|
||||||
|
|
||||||
|
Result - Optimal solution found (within gap tolerance)
|
||||||
|
|
||||||
|
Objective value: 308016.81415441
|
||||||
|
Lower bound: 308016.814
|
||||||
|
Gap: 0.00
|
||||||
|
Enumerated nodes: 3
|
||||||
|
Total iterations: 3022
|
||||||
|
Time (CPU seconds): 9.17
|
||||||
|
Time (Wallclock seconds): 9.37
|
||||||
|
|
||||||
|
Total time (CPU seconds): 9.97 (Wallclock seconds): 10.23
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
Solver command line: ['/usr/bin/cbc', '-seconds', '3200', '-allow', '2200', '-printingOptions', 'all', '-import', '/tmp/tmpvgmu8xky.pyomo.lp', '-stat=1', '-solve', '-solu', '/tmp/tmpvgmu8xky.pyomo.soln']
|
||||||
|
|
||||||
|
Welcome to the CBC MILP Solver
|
||||||
|
Version: 2.9.9
|
||||||
|
Build Date: Aug 7 2019
|
||||||
|
|
||||||
|
command line - /usr/bin/cbc -seconds 3200 -allow 2200 -printingOptions all -import /tmp/tmpvgmu8xky.pyomo.lp -stat=1 -solve -solu /tmp/tmpvgmu8xky.pyomo.soln (default strategy 1)
|
||||||
|
seconds was changed from 1e+100 to 3200
|
||||||
|
allowableGap was changed from 1e-10 to 2200
|
||||||
|
Option for printingOptions changed from normal to all
|
||||||
|
Presolve 61633 (-68602) rows, 24642 (-12955) columns and 252462 (-219179) elements
|
||||||
|
Statistics for presolved model
|
||||||
|
Original problem has 37336 integers (36504 of which binary)
|
||||||
|
Presolved problem has 24552 integers (24336 of which binary)
|
||||||
|
==== 21796 zero objective 3 different
|
||||||
|
21796 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2638 variables have objective of 1000
|
||||||
|
==== absolute objective values 3 different
|
||||||
|
21796 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2638 variables have objective of 1000
|
||||||
|
==== for integers 21796 zero objective 3 different
|
||||||
|
21796 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2548 variables have objective of 1000
|
||||||
|
==== for integers absolute objective values 3 different
|
||||||
|
21796 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2548 variables have objective of 1000
|
||||||
|
===== end objective counts
|
||||||
|
|
||||||
|
|
||||||
|
Problem has 61633 rows, 24642 columns (2846 with objective) and 252462 elements
|
||||||
|
There are 298 singletons with objective
|
||||||
|
Column breakdown:
|
||||||
|
306 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf,
|
||||||
|
0 of type lo->up, 0 of type free, 0 of type fixed,
|
||||||
|
0 of type -inf->0.0, 0 of type -inf->up, 24336 of type 0.0->1.0
|
||||||
|
Row breakdown:
|
||||||
|
2712 of type E 0.0, 0 of type E 1.0, 0 of type E -1.0,
|
||||||
|
409 of type E other, 0 of type G 0.0, 0 of type G 1.0,
|
||||||
|
376 of type G other, 4056 of type L 0.0, 49868 of type L 1.0,
|
||||||
|
1508 of type L other, 0 of type Range 0.0->1.0, 2704 of type Range other,
|
||||||
|
0 of type Free
|
||||||
|
Continuous objective value is 235275 - 18.65 seconds
|
||||||
|
Cgl0002I 9464 variables fixed
|
||||||
|
Cgl0003I 0 fixed, 208 tightened bounds, 37440 strengthened rows, 48132 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 830 strengthened rows, 0 substitutions
|
||||||
|
Cgl0004I processed model has 22157 rows, 15170 columns (15080 integer (14872 of which binary)) and 119840 elements
|
||||||
|
Cbc0038I Initial state - 627 integers unsatisfied sum - 209.255
|
||||||
|
Cbc0038I Pass 1: (27.97 seconds) suminf. 106.07246 (326) obj. 252225 iterations 3662
|
||||||
|
Cbc0038I Pass 2: (28.07 seconds) suminf. 95.72743 (291) obj. 251499 iterations 716
|
||||||
|
Cbc0038I Pass 3: (28.09 seconds) suminf. 93.33763 (280) obj. 251505 iterations 57
|
||||||
|
Cbc0038I Pass 4: (28.12 seconds) suminf. 92.83763 (279) obj. 251505 iterations 6
|
||||||
|
Cbc0038I Pass 5: (28.14 seconds) suminf. 92.83763 (279) obj. 251505 iterations 7
|
||||||
|
Cbc0038I Pass 6: (28.35 seconds) suminf. 88.07561 (260) obj. 271289 iterations 1630
|
||||||
|
Cbc0038I Pass 7: (28.57 seconds) suminf. 54.65876 (187) obj. 266649 iterations 1549
|
||||||
|
Cbc0038I Pass 8: (28.60 seconds) suminf. 51.21935 (179) obj. 267604 iterations 132
|
||||||
|
Cbc0038I Pass 9: (28.62 seconds) suminf. 51.21935 (179) obj. 267604 iterations 5
|
||||||
|
Cbc0038I Pass 10: (28.66 seconds) suminf. 45.87751 (162) obj. 267375 iterations 232
|
||||||
|
Cbc0038I Pass 11: (28.69 seconds) suminf. 41.50434 (150) obj. 267395 iterations 268
|
||||||
|
Cbc0038I Pass 12: (28.72 seconds) suminf. 38.95309 (137) obj. 267389 iterations 46
|
||||||
|
Cbc0038I Pass 13: (28.74 seconds) suminf. 37.01190 (134) obj. 267389 iterations 54
|
||||||
|
Cbc0038I Pass 14: (28.79 seconds) suminf. 32.20802 (125) obj. 267386 iterations 470
|
||||||
|
Cbc0038I Pass 15: (28.84 seconds) suminf. 31.56175 (131) obj. 267386 iterations 344
|
||||||
|
Cbc0038I Pass 16: (28.86 seconds) suminf. 31.56175 (131) obj. 267386 iterations 28
|
||||||
|
Cbc0038I Pass 17: (28.92 seconds) suminf. 29.43450 (121) obj. 268378 iterations 460
|
||||||
|
Cbc0038I Pass 18: (28.95 seconds) suminf. 29.26964 (118) obj. 268294 iterations 102
|
||||||
|
Cbc0038I Pass 19: (29.01 seconds) suminf. 29.44524 (120) obj. 268264 iterations 492
|
||||||
|
Cbc0038I Pass 20: (29.03 seconds) suminf. 29.44678 (122) obj. 268264 iterations 83
|
||||||
|
Cbc0038I Pass 21: (29.06 seconds) suminf. 29.36685 (125) obj. 267950 iterations 156
|
||||||
|
Cbc0038I Pass 22: (29.09 seconds) suminf. 29.34914 (124) obj. 267950 iterations 71
|
||||||
|
Cbc0038I Pass 23: (29.14 seconds) suminf. 29.92259 (124) obj. 267721 iterations 406
|
||||||
|
Cbc0038I Pass 24: (29.17 seconds) suminf. 29.77899 (123) obj. 267720 iterations 60
|
||||||
|
Cbc0038I Pass 25: (29.19 seconds) suminf. 29.77899 (123) obj. 267720 iterations 15
|
||||||
|
Cbc0038I Pass 26: (29.21 seconds) suminf. 29.77899 (123) obj. 267720 iterations 3
|
||||||
|
Cbc0038I Pass 27: (29.23 seconds) suminf. 29.77899 (123) obj. 267720 iterations 6
|
||||||
|
Cbc0038I Pass 28: (29.37 seconds) suminf. 42.90902 (137) obj. 287514 iterations 1303
|
||||||
|
Cbc0038I Pass 29: (29.51 seconds) suminf. 31.28342 (118) obj. 286853 iterations 1012
|
||||||
|
Cbc0038I Pass 30: (29.55 seconds) suminf. 30.17992 (120) obj. 287303 iterations 229
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 13786 integers at bound fixed and 43 continuous
|
||||||
|
Cbc0038I Mini branch and bound did not improve solution (29.58 seconds)
|
||||||
|
Cbc0038I After 29.58 seconds - Feasibility pump exiting - took 2.51 seconds
|
||||||
|
Cbc0031I 264 added rows had average density of 41.094697
|
||||||
|
Cbc0013I At root node, 264 cuts changed objective from 235274.72 to 247993.63 in 10 passes
|
||||||
|
Cbc0014I Cut generator 0 (Probing) - 729 row cuts average 6.8 elements, 0 column cuts (135 active) in 2.312 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 1 (Gomory) - 911 row cuts average 479.7 elements, 0 column cuts (0 active) in 2.646 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 2 (Knapsack) - 231 row cuts average 2.6 elements, 0 column cuts (0 active) in 0.145 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.034 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 186 row cuts average 57.1 elements, 0 column cuts (0 active) in 0.196 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.033 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 6 (TwoMirCuts) - 758 row cuts average 87.3 elements, 0 column cuts (0 active) in 1.797 seconds - new frequency is 1
|
||||||
|
Cbc0010I After 0 nodes, 1 on tree, 1e+50 best solution, best possible 247993.63 (51.80 seconds)
|
||||||
|
Cbc0012I Integer solution of 247993.63 found by rounding after 23120 iterations and 84 nodes (90.51 seconds)
|
||||||
|
Cbc0038I Full problem 22157 rows 15170 columns, reduced to 0 rows 0 columns
|
||||||
|
Cbc0001I Search completed - best objective 247993.6283088135, took 23120 iterations and 84 nodes (91.04 seconds)
|
||||||
|
Cbc0032I Strong branching done 1752 times (40035 iterations), fathomed 0 nodes and fixed 0 variables
|
||||||
|
Cbc0035I Maximum depth 50, 0 variables fixed on reduced cost
|
||||||
|
Cuts at root node changed objective from 235275 to 247994
|
||||||
|
Probing was tried 36 times and created 874 cuts of which 135 were active after adding rounds of cuts (2.553 seconds)
|
||||||
|
Gomory was tried 36 times and created 923 cuts of which 0 were active after adding rounds of cuts (3.186 seconds)
|
||||||
|
Knapsack was tried 36 times and created 280 cuts of which 0 were active after adding rounds of cuts (0.413 seconds)
|
||||||
|
Clique was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.034 seconds)
|
||||||
|
MixedIntegerRounding2 was tried 36 times and created 245 cuts of which 0 were active after adding rounds of cuts (0.652 seconds)
|
||||||
|
FlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.033 seconds)
|
||||||
|
TwoMirCuts was tried 36 times and created 821 cuts of which 0 were active after adding rounds of cuts (2.415 seconds)
|
||||||
|
ImplicationCuts was tried 16 times and created 90 cuts of which 0 were active after adding rounds of cuts (0.022 seconds)
|
||||||
|
|
||||||
|
Result - Optimal solution found
|
||||||
|
|
||||||
|
Objective value: 247993.62830881
|
||||||
|
Enumerated nodes: 84
|
||||||
|
Total iterations: 23120
|
||||||
|
Time (CPU seconds): 91.58
|
||||||
|
Time (Wallclock seconds): 91.98
|
||||||
|
|
||||||
|
Total time (CPU seconds): 92.49 (Wallclock seconds): 92.98
|
||||||
|
|
||||||
|
|
||||||
+167
@@ -0,0 +1,167 @@
|
|||||||
|
Solver command line: ['/usr/bin/cbc', '-seconds', '3200', '-allow', '2200', '-printingOptions', 'all', '-import', '/tmp/tmp5_964_on.pyomo.lp', '-stat=1', '-solve', '-solu', '/tmp/tmp5_964_on.pyomo.soln']
|
||||||
|
|
||||||
|
Welcome to the CBC MILP Solver
|
||||||
|
Version: 2.9.9
|
||||||
|
Build Date: Aug 7 2019
|
||||||
|
|
||||||
|
command line - /usr/bin/cbc -seconds 3200 -allow 2200 -printingOptions all -import /tmp/tmp5_964_on.pyomo.lp -stat=1 -solve -solu /tmp/tmp5_964_on.pyomo.soln (default strategy 1)
|
||||||
|
seconds was changed from 1e+100 to 3200
|
||||||
|
allowableGap was changed from 1e-10 to 2200
|
||||||
|
Option for printingOptions changed from normal to all
|
||||||
|
Presolve 9073 (-28732) rows, 13610 (-73595) columns and 64596 (-446201) elements
|
||||||
|
Statistics for presolved model
|
||||||
|
Original problem has 86684 integers (86528 of which binary)
|
||||||
|
Presolved problem has 13520 integers (13520 of which binary)
|
||||||
|
==== 10894 zero objective 3 different
|
||||||
|
10894 variables have objective of 0
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1364 variables have objective of 1000
|
||||||
|
==== absolute objective values 3 different
|
||||||
|
10894 variables have objective of 0
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1364 variables have objective of 1000
|
||||||
|
==== for integers 10894 zero objective 3 different
|
||||||
|
10894 variables have objective of 0
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1274 variables have objective of 1000
|
||||||
|
==== for integers absolute objective values 3 different
|
||||||
|
10894 variables have objective of 0
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1274 variables have objective of 1000
|
||||||
|
===== end objective counts
|
||||||
|
|
||||||
|
|
||||||
|
Problem has 9073 rows, 13610 columns (2716 with objective) and 64596 elements
|
||||||
|
There are 1442 singletons with objective
|
||||||
|
Column breakdown:
|
||||||
|
90 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf,
|
||||||
|
0 of type lo->up, 0 of type free, 0 of type fixed,
|
||||||
|
0 of type -inf->0.0, 0 of type -inf->up, 13520 of type 0.0->1.0
|
||||||
|
Row breakdown:
|
||||||
|
0 of type E 0.0, 676 of type E 1.0, 0 of type E -1.0,
|
||||||
|
97 of type E other, 0 of type G 0.0, 0 of type G 1.0,
|
||||||
|
84 of type G other, 5408 of type L 0.0, 1352 of type L 1.0,
|
||||||
|
1456 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other,
|
||||||
|
0 of type Free
|
||||||
|
Continuous objective value is 5579.29 - 0.25 seconds
|
||||||
|
Cgl0002I 66248 variables fixed
|
||||||
|
Cgl0003I 52 fixed, 0 tightened bounds, 4004 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 196 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1533 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1527 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1527 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1524 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1522 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1520 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1520 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1410 strengthened rows, 0 substitutions
|
||||||
|
Cgl0004I processed model has 8929 rows, 13558 columns (13468 integer (13468 of which binary)) and 72963 elements
|
||||||
|
Cbc0038I Initial state - 757 integers unsatisfied sum - 322.142
|
||||||
|
Cbc0038I Pass 1: (2.10 seconds) suminf. 231.92695 (505) obj. 30728.2 iterations 1751
|
||||||
|
Cbc0038I Pass 2: (2.12 seconds) suminf. 206.58889 (444) obj. 31931.6 iterations 194
|
||||||
|
Cbc0038I Pass 3: (2.14 seconds) suminf. 196.54920 (421) obj. 32116 iterations 204
|
||||||
|
Cbc0038I Pass 4: (2.16 seconds) suminf. 196.54920 (421) obj. 32116 iterations 37
|
||||||
|
Cbc0038I Pass 5: (2.18 seconds) suminf. 195.04920 (418) obj. 32166 iterations 17
|
||||||
|
Cbc0038I Pass 6: (2.20 seconds) suminf. 195.04920 (418) obj. 32166 iterations 24
|
||||||
|
Cbc0038I Pass 7: (2.22 seconds) suminf. 195.04920 (418) obj. 32166 iterations 18
|
||||||
|
Cbc0038I Pass 8: (2.25 seconds) suminf. 72.58889 (171) obj. 37485.9 iterations 561
|
||||||
|
Cbc0038I Pass 9: (2.27 seconds) suminf. 70.08889 (166) obj. 37485.9 iterations 32
|
||||||
|
Cbc0038I Pass 10: (2.29 seconds) suminf. 69.08889 (164) obj. 37485.9 iterations 6
|
||||||
|
Cbc0038I Pass 11: (2.31 seconds) suminf. 69.08889 (164) obj. 37485.9 iterations 4
|
||||||
|
Cbc0038I Pass 12: (2.35 seconds) suminf. 32.58141 (84) obj. 60503.3 iterations 914
|
||||||
|
Cbc0038I Pass 13: (2.39 seconds) suminf. 27.88537 (75) obj. 61927.9 iterations 484
|
||||||
|
Cbc0038I Pass 14: (2.41 seconds) suminf. 27.50124 (75) obj. 62180.1 iterations 58
|
||||||
|
Cbc0038I Pass 15: (2.43 seconds) suminf. 27.00124 (74) obj. 62180.1 iterations 1
|
||||||
|
Cbc0038I Pass 16: (2.45 seconds) suminf. 16.00124 (52) obj. 62980.1 iterations 32
|
||||||
|
Cbc0038I Pass 17: (2.47 seconds) suminf. 7.10271 (34) obj. 62816.5 iterations 296
|
||||||
|
Cbc0038I Pass 18: (2.49 seconds) suminf. 6.79422 (33) obj. 62168.7 iterations 55
|
||||||
|
Cbc0038I Pass 19: (2.53 seconds) suminf. 3.14487 (17) obj. 64662.8 iterations 509
|
||||||
|
Cbc0038I Pass 20: (2.55 seconds) suminf. 3.14487 (17) obj. 64662.8 iterations 192
|
||||||
|
Cbc0038I Pass 21: (2.59 seconds) suminf. 1.23209 (8) obj. 65540 iterations 475
|
||||||
|
Cbc0038I Pass 22: (2.61 seconds) suminf. 1.23209 (8) obj. 65540 iterations 82
|
||||||
|
Cbc0038I Pass 23: (2.63 seconds) suminf. 0.79917 (6) obj. 66277.4 iterations 327
|
||||||
|
Cbc0038I Pass 24: (2.66 seconds) suminf. 1.01893 (6) obj. 65540 iterations 189
|
||||||
|
Cbc0038I Pass 25: (2.69 seconds) suminf. 14.01747 (45) obj. 83423 iterations 774
|
||||||
|
Cbc0038I Pass 26: (2.73 seconds) suminf. 10.55675 (34) obj. 83054.5 iterations 517
|
||||||
|
Cbc0038I Pass 27: (2.75 seconds) suminf. 9.55675 (32) obj. 83054.5 iterations 5
|
||||||
|
Cbc0038I Pass 28: (2.77 seconds) suminf. 9.55675 (32) obj. 83054.5 iterations 6
|
||||||
|
Cbc0038I Pass 29: (2.79 seconds) suminf. 2.31454 (11) obj. 81144.3 iterations 317
|
||||||
|
Cbc0038I Pass 30: (2.81 seconds) suminf. 2.27485 (10) obj. 81546.8 iterations 84
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 12405 integers at bound fixed and 20 continuous
|
||||||
|
Cbc0038I Full problem 8929 rows 13558 columns, reduced to 355 rows 402 columns
|
||||||
|
Cbc0038I Mini branch and bound improved solution from 1.79769e+308 to 44152.7 (3.07 seconds)
|
||||||
|
Cbc0038I Freeing continuous variables gives a solution of 44152.7
|
||||||
|
Cbc0038I Round again with cutoff of 41270.3
|
||||||
|
Cbc0038I Pass 30: (3.10 seconds) suminf. 231.92695 (505) obj. 30728.2 iterations 0
|
||||||
|
Cbc0038I Pass 31: (3.13 seconds) suminf. 186.69839 (405) obj. 34976.8 iterations 320
|
||||||
|
Cbc0038I Pass 32: (3.15 seconds) suminf. 175.89364 (383) obj. 35626.8 iterations 89
|
||||||
|
Cbc0038I Pass 33: (3.17 seconds) suminf. 174.39364 (380) obj. 35676.8 iterations 10
|
||||||
|
Cbc0038I Pass 34: (3.18 seconds) suminf. 174.39364 (380) obj. 35676.8 iterations 3
|
||||||
|
Cbc0038I Pass 35: (3.20 seconds) suminf. 165.39364 (362) obj. 36526.8 iterations 35
|
||||||
|
Cbc0038I Pass 36: (3.22 seconds) suminf. 152.89364 (337) obj. 37726.8 iterations 41
|
||||||
|
Cbc0038I Pass 37: (3.24 seconds) suminf. 137.89364 (307) obj. 39126.8 iterations 44
|
||||||
|
Cbc0038I Pass 38: (3.27 seconds) suminf. 132.89364 (297) obj. 39626.8 iterations 15
|
||||||
|
Cbc0038I Pass 39: (3.29 seconds) suminf. 117.39364 (266) obj. 40976.8 iterations 44
|
||||||
|
Cbc0038I Pass 40: (3.33 seconds) suminf. 106.89364 (251) obj. 41270.3 iterations 184
|
||||||
|
Cbc0038I Pass 41: (3.36 seconds) suminf. 99.39364 (236) obj. 41270.3 iterations 71
|
||||||
|
Cbc0038I Pass 42: (3.40 seconds) suminf. 93.04601 (226) obj. 41270.3 iterations 176
|
||||||
|
Cbc0038I Pass 43: (3.43 seconds) suminf. 92.89364 (223) obj. 41270.3 iterations 135
|
||||||
|
Cbc0038I Pass 44: (3.46 seconds) suminf. 92.39364 (222) obj. 41270.3 iterations 3
|
||||||
|
Cbc0038I Pass 45: (3.49 seconds) suminf. 83.89364 (208) obj. 41270.3 iterations 74
|
||||||
|
Cbc0038I Pass 46: (3.51 seconds) suminf. 70.89364 (182) obj. 41270.3 iterations 44
|
||||||
|
Cbc0038I Pass 47: (3.53 seconds) suminf. 63.89364 (168) obj. 41270.3 iterations 26
|
||||||
|
Cbc0038I Pass 48: (3.55 seconds) suminf. 63.89364 (168) obj. 41270.3 iterations 4
|
||||||
|
Cbc0038I Pass 49: (3.58 seconds) suminf. 57.89364 (156) obj. 41270.3 iterations 28
|
||||||
|
Cbc0038I Pass 50: (3.60 seconds) suminf. 57.89364 (156) obj. 41270.3 iterations 2
|
||||||
|
Cbc0038I Pass 51: (3.62 seconds) suminf. 45.39364 (131) obj. 41270.3 iterations 33
|
||||||
|
Cbc0038I Pass 52: (3.64 seconds) suminf. 32.89364 (106) obj. 41270.3 iterations 42
|
||||||
|
Cbc0038I Pass 53: (3.66 seconds) suminf. 25.39364 (91) obj. 41270.3 iterations 34
|
||||||
|
Cbc0038I Pass 54: (3.68 seconds) suminf. 14.89364 (70) obj. 41270.3 iterations 35
|
||||||
|
Cbc0038I Pass 55: (3.73 seconds) suminf. 9.96998 (47) obj. 41270.3 iterations 460
|
||||||
|
Cbc0038I Pass 56: (3.77 seconds) suminf. 8.19475 (45) obj. 41270.3 iterations 185
|
||||||
|
Cbc0038I Pass 57: (3.84 seconds) suminf. 6.00263 (32) obj. 41270.3 iterations 797
|
||||||
|
Cbc0038I Pass 58: (3.87 seconds) suminf. 5.48991 (33) obj. 41270.3 iterations 238
|
||||||
|
Cbc0038I Pass 59: (3.93 seconds) suminf. 4.62934 (19) obj. 40801.3 iterations 623
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 12628 integers at bound fixed and 39 continuous
|
||||||
|
Cbc0038I Mini branch and bound did not improve solution (3.98 seconds)
|
||||||
|
Cbc0038I After 3.98 seconds - Feasibility pump exiting with objective of 44152.7 - took 1.99 seconds
|
||||||
|
Cbc0012I Integer solution of 44152.66 found by feasibility pump after 0 iterations and 0 nodes (3.99 seconds)
|
||||||
|
Cbc0038I Full problem 8929 rows 13558 columns, reduced to 228 rows 243 columns
|
||||||
|
Cbc0012I Integer solution of 40453.232 found by RINS after 0 iterations and 0 nodes (4.21 seconds)
|
||||||
|
Cbc0012I Integer solution of 29020.952 found by DiveCoefficient after 8819 iterations and 0 nodes (11.10 seconds)
|
||||||
|
Cbc0031I 340 added rows had average density of 48.729412
|
||||||
|
Cbc0013I At root node, 340 cuts changed objective from 15329.289 to 27320.952 in 10 passes
|
||||||
|
Cbc0014I Cut generator 0 (Probing) - 2358 row cuts average 2.0 elements, 0 column cuts (0 active) in 1.816 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 1 (Gomory) - 2006 row cuts average 335.9 elements, 0 column cuts (0 active) in 1.068 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 2 (Knapsack) - 1313 row cuts average 2.1 elements, 0 column cuts (0 active) in 0.138 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.011 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 1050 row cuts average 17.3 elements, 0 column cuts (0 active) in 0.129 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.029 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 6 (TwoMirCuts) - 2500 row cuts average 26.5 elements, 0 column cuts (0 active) in 1.001 seconds - new frequency is 1
|
||||||
|
Cbc0011I Exiting as integer gap of 1700 less than 2200 or 0%%
|
||||||
|
Cbc0001I Search completed - best objective 29020.95202118316, took 8819 iterations and 0 nodes (11.12 seconds)
|
||||||
|
Cbc0035I Maximum depth 0, 0 variables fixed on reduced cost
|
||||||
|
Cuts at root node changed objective from 15329.3 to 27321
|
||||||
|
Probing was tried 10 times and created 2358 cuts of which 0 were active after adding rounds of cuts (1.816 seconds)
|
||||||
|
Gomory was tried 10 times and created 2006 cuts of which 0 were active after adding rounds of cuts (1.068 seconds)
|
||||||
|
Knapsack was tried 10 times and created 1313 cuts of which 0 were active after adding rounds of cuts (0.138 seconds)
|
||||||
|
Clique was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.011 seconds)
|
||||||
|
MixedIntegerRounding2 was tried 10 times and created 1050 cuts of which 0 were active after adding rounds of cuts (0.129 seconds)
|
||||||
|
FlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.029 seconds)
|
||||||
|
TwoMirCuts was tried 10 times and created 2500 cuts of which 0 were active after adding rounds of cuts (1.001 seconds)
|
||||||
|
|
||||||
|
Result - Optimal solution found (within gap tolerance)
|
||||||
|
|
||||||
|
Objective value: 29020.95202118
|
||||||
|
Lower bound: 27320.952
|
||||||
|
Gap: 0.06
|
||||||
|
Enumerated nodes: 0
|
||||||
|
Total iterations: 8819
|
||||||
|
Time (CPU seconds): 11.49
|
||||||
|
Time (Wallclock seconds): 11.67
|
||||||
|
|
||||||
|
Total time (CPU seconds): 12.70 (Wallclock seconds): 12.97
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
Solver command line: ['/usr/bin/cbc', '-seconds', '3200', '-allow', '2200', '-printingOptions', 'all', '-import', '/tmp/tmpkl4fr1k_.pyomo.lp', '-stat=1', '-solve', '-solu', '/tmp/tmpkl4fr1k_.pyomo.soln']
|
||||||
|
|
||||||
|
Welcome to the CBC MILP Solver
|
||||||
|
Version: 2.9.9
|
||||||
|
Build Date: Aug 7 2019
|
||||||
|
|
||||||
|
command line - /usr/bin/cbc -seconds 3200 -allow 2200 -printingOptions all -import /tmp/tmpkl4fr1k_.pyomo.lp -stat=1 -solve -solu /tmp/tmpkl4fr1k_.pyomo.soln (default strategy 1)
|
||||||
|
seconds was changed from 1e+100 to 3200
|
||||||
|
allowableGap was changed from 1e-10 to 2200
|
||||||
|
Option for printingOptions changed from normal to all
|
||||||
|
Presolve 37323 (-99282) rows, 19234 (-52423) columns and 162892 (-464645) elements
|
||||||
|
Statistics for presolved model
|
||||||
|
Original problem has 71240 integers (70304 of which binary)
|
||||||
|
Presolved problem has 19144 integers (18928 of which binary)
|
||||||
|
==== 15946 zero objective 4 different
|
||||||
|
15946 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1728 variables have objective of 1000
|
||||||
|
==== absolute objective values 4 different
|
||||||
|
15946 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1728 variables have objective of 1000
|
||||||
|
==== for integers 15946 zero objective 4 different
|
||||||
|
15946 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1638 variables have objective of 1000
|
||||||
|
==== for integers absolute objective values 4 different
|
||||||
|
15946 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
1352 variables have objective of 100
|
||||||
|
1638 variables have objective of 1000
|
||||||
|
===== end objective counts
|
||||||
|
|
||||||
|
|
||||||
|
Problem has 37323 rows, 19234 columns (3288 with objective) and 162892 elements
|
||||||
|
There are 1650 singletons with objective
|
||||||
|
Column breakdown:
|
||||||
|
306 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf,
|
||||||
|
0 of type lo->up, 0 of type free, 0 of type fixed,
|
||||||
|
0 of type -inf->0.0, 0 of type -inf->up, 18928 of type 0.0->1.0
|
||||||
|
Row breakdown:
|
||||||
|
2712 of type E 0.0, 156 of type E 1.0, 0 of type E -1.0,
|
||||||
|
279 of type E other, 0 of type G 0.0, 0 of type G 1.0,
|
||||||
|
376 of type G other, 5408 of type L 0.0, 24180 of type L 1.0,
|
||||||
|
1508 of type L other, 0 of type Range 0.0->1.0, 2704 of type Range other,
|
||||||
|
0 of type Free
|
||||||
|
Continuous objective value is 243935 - 12.03 seconds
|
||||||
|
Cgl0002I 44616 variables fixed
|
||||||
|
Cgl0003I 0 fixed, 208 tightened bounds, 22826 strengthened rows, 47235 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 3826 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 2221 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1079 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 490 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 255 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 229 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 214 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 197 strengthened rows, 0 substitutions
|
||||||
|
Cgl0004I processed model has 16387 rows, 9762 columns (9672 integer (9464 of which binary)) and 80599 elements
|
||||||
|
Cbc0038I Initial state - 1275 integers unsatisfied sum - 411.736
|
||||||
|
Cbc0038I Pass 1: (22.09 seconds) suminf. 275.36033 (699) obj. 275508 iterations 6394
|
||||||
|
Cbc0038I Pass 2: (22.31 seconds) suminf. 251.50450 (630) obj. 278893 iterations 1211
|
||||||
|
Cbc0038I Pass 3: (22.42 seconds) suminf. 237.26278 (596) obj. 280250 iterations 470
|
||||||
|
Cbc0038I Pass 4: (22.50 seconds) suminf. 231.25228 (584) obj. 281559 iterations 306
|
||||||
|
Cbc0038I Pass 5: (22.56 seconds) suminf. 228.30447 (580) obj. 283466 iterations 239
|
||||||
|
Cbc0038I Pass 6: (22.60 seconds) suminf. 224.54385 (568) obj. 283611 iterations 52
|
||||||
|
Cbc0038I Pass 7: (22.64 seconds) suminf. 220.41141 (557) obj. 281841 iterations 75
|
||||||
|
Cbc0038I Pass 8: (22.67 seconds) suminf. 219.91141 (556) obj. 281891 iterations 18
|
||||||
|
Cbc0038I Pass 9: (22.70 seconds) suminf. 216.62887 (548) obj. 278935 iterations 28
|
||||||
|
Cbc0038I Pass 10: (22.74 seconds) suminf. 216.62887 (548) obj. 278935 iterations 11
|
||||||
|
Cbc0038I Pass 11: (22.77 seconds) suminf. 216.62887 (548) obj. 278935 iterations 1
|
||||||
|
Cbc0038I Pass 12: (22.96 seconds) suminf. 104.93793 (325) obj. 277541 iterations 1105
|
||||||
|
Cbc0038I Pass 13: (23.22 seconds) suminf. 89.97792 (266) obj. 277060 iterations 1082
|
||||||
|
Cbc0038I Pass 14: (23.40 seconds) suminf. 87.18147 (261) obj. 276745 iterations 761
|
||||||
|
Cbc0038I Pass 15: (23.44 seconds) suminf. 83.46793 (252) obj. 276810 iterations 30
|
||||||
|
Cbc0038I Pass 16: (23.47 seconds) suminf. 82.96793 (251) obj. 276860 iterations 19
|
||||||
|
Cbc0038I Pass 17: (23.54 seconds) suminf. 78.18433 (247) obj. 277277 iterations 204
|
||||||
|
Cbc0038I Pass 18: (23.58 seconds) suminf. 77.57806 (243) obj. 277327 iterations 100
|
||||||
|
Cbc0038I Pass 19: (23.62 seconds) suminf. 67.34112 (222) obj. 278330 iterations 84
|
||||||
|
Cbc0038I Pass 20: (23.68 seconds) suminf. 65.92745 (219) obj. 278478 iterations 148
|
||||||
|
Cbc0038I Pass 21: (23.72 seconds) suminf. 65.98731 (206) obj. 278632 iterations 50
|
||||||
|
Cbc0038I Pass 22: (23.75 seconds) suminf. 65.48731 (205) obj. 278632 iterations 2
|
||||||
|
Cbc0038I Pass 23: (23.79 seconds) suminf. 50.98731 (175) obj. 279728 iterations 96
|
||||||
|
Cbc0038I Pass 24: (23.82 seconds) suminf. 50.98731 (175) obj. 279728 iterations 1
|
||||||
|
Cbc0038I Pass 25: (23.86 seconds) suminf. 38.98731 (148) obj. 279915 iterations 83
|
||||||
|
Cbc0038I Pass 26: (24.08 seconds) suminf. 35.45286 (143) obj. 281045 iterations 828
|
||||||
|
Cbc0038I Pass 27: (24.14 seconds) suminf. 35.45286 (145) obj. 281045 iterations 136
|
||||||
|
Cbc0038I Pass 28: (24.22 seconds) suminf. 33.71076 (144) obj. 281155 iterations 337
|
||||||
|
Cbc0038I Pass 29: (24.26 seconds) suminf. 33.71076 (144) obj. 281155 iterations 47
|
||||||
|
Cbc0038I Pass 30: (24.45 seconds) suminf. 33.05241 (137) obj. 281075 iterations 685
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 7967 integers at bound fixed and 43 continuous
|
||||||
|
Cbc0038I Mini branch and bound did not improve solution (24.48 seconds)
|
||||||
|
Cbc0038I After 24.48 seconds - Feasibility pump exiting - took 4.32 seconds
|
||||||
|
Cbc0031I 631 added rows had average density of 14.393027
|
||||||
|
Cbc0013I At root node, 631 cuts changed objective from 253685.18 to 271905.7 in 10 passes
|
||||||
|
Cbc0014I Cut generator 0 (Probing) - 3898 row cuts average 3.2 elements, 0 column cuts (245 active) in 1.358 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 1 (Gomory) - 2171 row cuts average 144.4 elements, 0 column cuts (0 active) in 3.571 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 2 (Knapsack) - 2247 row cuts average 2.3 elements, 0 column cuts (0 active) in 0.210 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.071 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 1162 row cuts average 6.3 elements, 0 column cuts (0 active) in 0.142 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.022 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 6 (TwoMirCuts) - 3316 row cuts average 32.5 elements, 0 column cuts (0 active) in 2.821 seconds - new frequency is 1
|
||||||
|
Cbc0010I After 0 nodes, 1 on tree, 1e+50 best solution, best possible 271905.7 (55.94 seconds)
|
||||||
|
Cbc0010I After 100 nodes, 57 on tree, 1e+50 best solution, best possible 271905.7 (157.36 seconds)
|
||||||
|
Cbc0010I After 200 nodes, 109 on tree, 1e+50 best solution, best possible 271905.7 (202.91 seconds)
|
||||||
|
Cbc0010I After 300 nodes, 159 on tree, 1e+50 best solution, best possible 271905.7 (246.36 seconds)
|
||||||
|
Cbc0010I After 400 nodes, 209 on tree, 1e+50 best solution, best possible 271905.7 (282.02 seconds)
|
||||||
|
Cbc0010I After 500 nodes, 261 on tree, 1e+50 best solution, best possible 271905.7 (316.04 seconds)
|
||||||
|
Cbc0010I After 600 nodes, 310 on tree, 1e+50 best solution, best possible 271905.7 (349.89 seconds)
|
||||||
|
Cbc0010I After 700 nodes, 359 on tree, 1e+50 best solution, best possible 271905.7 (379.69 seconds)
|
||||||
|
Cbc0010I After 800 nodes, 409 on tree, 1e+50 best solution, best possible 271905.7 (411.29 seconds)
|
||||||
|
Cbc0010I After 900 nodes, 459 on tree, 1e+50 best solution, best possible 271905.7 (445.93 seconds)
|
||||||
|
Cbc0010I After 1000 nodes, 509 on tree, 1e+50 best solution, best possible 271905.7 (480.64 seconds)
|
||||||
|
Cbc0010I After 1100 nodes, 608 on tree, 1e+50 best solution, best possible 271905.7 (499.37 seconds)
|
||||||
|
Cbc0010I After 1200 nodes, 708 on tree, 1e+50 best solution, best possible 271905.7 (520.89 seconds)
|
||||||
|
Cbc0010I After 1300 nodes, 808 on tree, 1e+50 best solution, best possible 271905.7 (537.68 seconds)
|
||||||
|
Cbc0010I After 1400 nodes, 908 on tree, 1e+50 best solution, best possible 271905.7 (552.14 seconds)
|
||||||
|
Cbc0012I Integer solution of 272705.7 found by rounding after 448734 iterations and 1452 nodes (559.78 seconds)
|
||||||
|
Cbc0038I Full problem 16387 rows 9762 columns, reduced to 0 rows 0 columns
|
||||||
|
Cbc0011I Exiting as integer gap of 800 less than 2200 or 0%%
|
||||||
|
Cbc0001I Search completed - best objective 272705.7047293056, took 448997 iterations and 1453 nodes (561.24 seconds)
|
||||||
|
Cbc0032I Strong branching done 6808 times (449919 iterations), fathomed 0 nodes and fixed 2 variables
|
||||||
|
Cbc0035I Maximum depth 523, 518 variables fixed on reduced cost
|
||||||
|
Cuts at root node changed objective from 253685 to 271906
|
||||||
|
Probing was tried 449 times and created 36967 cuts of which 245 were active after adding rounds of cuts (4.132 seconds)
|
||||||
|
Gomory was tried 449 times and created 14449 cuts of which 0 were active after adding rounds of cuts (17.213 seconds)
|
||||||
|
Knapsack was tried 449 times and created 26037 cuts of which 0 were active after adding rounds of cuts (5.361 seconds)
|
||||||
|
Clique was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.071 seconds)
|
||||||
|
MixedIntegerRounding2 was tried 449 times and created 15587 cuts of which 0 were active after adding rounds of cuts (5.479 seconds)
|
||||||
|
FlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.022 seconds)
|
||||||
|
TwoMirCuts was tried 449 times and created 21730 cuts of which 0 were active after adding rounds of cuts (6.315 seconds)
|
||||||
|
ImplicationCuts was tried 19 times and created 3254 cuts of which 0 were active after adding rounds of cuts (0.035 seconds)
|
||||||
|
|
||||||
|
Result - Optimal solution found (within gap tolerance)
|
||||||
|
|
||||||
|
Objective value: 272705.70472931
|
||||||
|
Lower bound: 271905.705
|
||||||
|
Gap: 0.00
|
||||||
|
Enumerated nodes: 1453
|
||||||
|
Total iterations: 448997
|
||||||
|
Time (CPU seconds): 562.33
|
||||||
|
Time (Wallclock seconds): 563.34
|
||||||
|
|
||||||
|
Total time (CPU seconds): 563.79 (Wallclock seconds): 564.95
|
||||||
|
|
||||||
|
|
||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
Solver command line: ['/usr/bin/cbc', '-seconds', '3200', '-allow', '2200', '-printingOptions', 'all', '-import', '/tmp/tmpkhpv9a3u.pyomo.lp', '-stat=1', '-solve', '-solu', '/tmp/tmpkhpv9a3u.pyomo.soln']
|
||||||
|
|
||||||
|
Welcome to the CBC MILP Solver
|
||||||
|
Version: 2.9.9
|
||||||
|
Build Date: Aug 7 2019
|
||||||
|
|
||||||
|
command line - /usr/bin/cbc -seconds 3200 -allow 2200 -printingOptions all -import /tmp/tmpkhpv9a3u.pyomo.lp -stat=1 -solve -solu /tmp/tmpkhpv9a3u.pyomo.soln (default strategy 1)
|
||||||
|
seconds was changed from 1e+100 to 3200
|
||||||
|
allowableGap was changed from 1e-10 to 2200
|
||||||
|
Option for printingOptions changed from normal to all
|
||||||
|
Presolve 51155 (-79938) rows, 21938 (-44207) columns and 202022 (-585467) elements
|
||||||
|
Statistics for presolved model
|
||||||
|
Original problem has 65728 integers (64896 of which binary)
|
||||||
|
Presolved problem has 21848 integers (21632 of which binary)
|
||||||
|
==== 19456 zero objective 3 different
|
||||||
|
19456 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2274 variables have objective of 1000
|
||||||
|
==== absolute objective values 3 different
|
||||||
|
19456 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2274 variables have objective of 1000
|
||||||
|
==== for integers 19456 zero objective 3 different
|
||||||
|
19456 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2184 variables have objective of 1000
|
||||||
|
==== for integers absolute objective values 3 different
|
||||||
|
19456 variables have objective of 0
|
||||||
|
208 variables have objective of 20
|
||||||
|
2184 variables have objective of 1000
|
||||||
|
===== end objective counts
|
||||||
|
|
||||||
|
|
||||||
|
Problem has 51155 rows, 21938 columns (2482 with objective) and 202022 elements
|
||||||
|
There are 298 singletons with objective
|
||||||
|
Column breakdown:
|
||||||
|
306 of type 0.0->inf, 0 of type 0.0->up, 0 of type lo->inf,
|
||||||
|
0 of type lo->up, 0 of type free, 0 of type fixed,
|
||||||
|
0 of type -inf->0.0, 0 of type -inf->up, 21632 of type 0.0->1.0
|
||||||
|
Row breakdown:
|
||||||
|
2712 of type E 0.0, 520 of type E 1.0, 0 of type E -1.0,
|
||||||
|
227 of type E other, 0 of type G 0.0, 0 of type G 1.0,
|
||||||
|
376 of type G other, 4056 of type L 0.0, 41756 of type L 1.0,
|
||||||
|
1508 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other,
|
||||||
|
0 of type Free
|
||||||
|
Continuous objective value is 239817 - 9.92 seconds
|
||||||
|
Cgl0002I 40560 variables fixed
|
||||||
|
Cgl0003I 0 fixed, 208 tightened bounds, 31984 strengthened rows, 69557 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 4437 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 2091 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 1067 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 143 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 135 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 126 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 120 strengthened rows, 0 substitutions
|
||||||
|
Cgl0003I 0 fixed, 0 tightened bounds, 113 strengthened rows, 0 substitutions
|
||||||
|
Cgl0004I processed model has 10896 rows, 9762 columns (9672 integer (9464 of which binary)) and 72490 elements
|
||||||
|
Cbc0038I Initial state - 346 integers unsatisfied sum - 76.2913
|
||||||
|
Cbc0038I Pass 1: (15.76 seconds) suminf. 33.26604 (123) obj. 249238 iterations 1545
|
||||||
|
Cbc0038I Pass 2: (15.78 seconds) suminf. 25.78093 (112) obj. 251318 iterations 269
|
||||||
|
Cbc0038I Pass 3: (15.80 seconds) suminf. 25.79137 (111) obj. 251456 iterations 69
|
||||||
|
Cbc0038I Pass 4: (15.82 seconds) suminf. 21.19438 (98) obj. 249900 iterations 423
|
||||||
|
Cbc0038I Pass 5: (15.84 seconds) suminf. 21.83309 (100) obj. 250336 iterations 56
|
||||||
|
Cbc0038I Pass 6: (15.86 seconds) suminf. 20.71492 (94) obj. 249739 iterations 149
|
||||||
|
Cbc0038I Pass 7: (15.88 seconds) suminf. 20.09167 (98) obj. 252364 iterations 33
|
||||||
|
Cbc0038I Pass 8: (15.91 seconds) suminf. 19.76241 (84) obj. 250731 iterations 323
|
||||||
|
Cbc0038I Pass 9: (15.93 seconds) suminf. 19.49844 (84) obj. 251114 iterations 93
|
||||||
|
Cbc0038I Pass 10: (15.95 seconds) suminf. 21.05958 (87) obj. 250700 iterations 261
|
||||||
|
Cbc0038I Pass 11: (15.96 seconds) suminf. 19.48336 (84) obj. 253142 iterations 83
|
||||||
|
Cbc0038I Pass 12: (15.98 seconds) suminf. 18.90177 (82) obj. 253142 iterations 11
|
||||||
|
Cbc0038I Pass 13: (16.00 seconds) suminf. 19.56423 (80) obj. 250761 iterations 260
|
||||||
|
Cbc0038I Pass 14: (16.02 seconds) suminf. 19.58715 (83) obj. 251156 iterations 97
|
||||||
|
Cbc0038I Pass 15: (16.04 seconds) suminf. 20.75833 (83) obj. 250573 iterations 263
|
||||||
|
Cbc0038I Pass 16: (16.06 seconds) suminf. 20.45839 (85) obj. 250971 iterations 81
|
||||||
|
Cbc0038I Pass 17: (16.08 seconds) suminf. 19.69155 (83) obj. 250561 iterations 141
|
||||||
|
Cbc0038I Pass 18: (16.09 seconds) suminf. 19.43441 (86) obj. 250946 iterations 40
|
||||||
|
Cbc0038I Pass 19: (16.11 seconds) suminf. 21.02156 (80) obj. 250564 iterations 260
|
||||||
|
Cbc0038I Pass 20: (16.13 seconds) suminf. 17.85922 (77) obj. 250965 iterations 106
|
||||||
|
Cbc0038I Pass 21: (16.15 seconds) suminf. 17.45928 (78) obj. 250601 iterations 200
|
||||||
|
Cbc0038I Pass 22: (16.17 seconds) suminf. 17.92720 (78) obj. 250994 iterations 212
|
||||||
|
Cbc0038I Pass 23: (16.19 seconds) suminf. 21.11487 (78) obj. 250598 iterations 209
|
||||||
|
Cbc0038I Pass 24: (16.21 seconds) suminf. 19.59336 (79) obj. 250987 iterations 67
|
||||||
|
Cbc0038I Pass 25: (16.23 seconds) suminf. 18.27678 (74) obj. 250985 iterations 255
|
||||||
|
Cbc0038I Pass 26: (16.24 seconds) suminf. 18.38292 (78) obj. 251377 iterations 30
|
||||||
|
Cbc0038I Pass 27: (16.26 seconds) suminf. 17.97511 (82) obj. 251341 iterations 129
|
||||||
|
Cbc0038I Pass 28: (16.28 seconds) suminf. 17.97511 (81) obj. 251174 iterations 8
|
||||||
|
Cbc0038I Pass 29: (16.30 seconds) suminf. 17.12085 (75) obj. 250970 iterations 164
|
||||||
|
Cbc0038I Pass 30: (16.31 seconds) suminf. 16.96998 (77) obj. 251356 iterations 34
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 9127 integers at bound fixed and 42 continuous
|
||||||
|
Cbc0038I Full problem 10896 rows 9762 columns, reduced to 514 rows 451 columns
|
||||||
|
Cbc0038I Mini branch and bound did not improve solution (16.47 seconds)
|
||||||
|
Cbc0038I After 16.47 seconds - Feasibility pump exiting - took 0.94 seconds
|
||||||
|
Cbc0012I Integer solution of 258618.43 found by DiveCoefficient after 3400 iterations and 0 nodes (23.00 seconds)
|
||||||
|
Cbc0031I 95 added rows had average density of 558.45263
|
||||||
|
Cbc0013I At root node, 95 cuts changed objective from 239816.84 to 258618.43 in 10 passes
|
||||||
|
Cbc0014I Cut generator 0 (Probing) - 181 row cuts average 21.4 elements, 0 column cuts (0 active) in 2.128 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 1 (Gomory) - 301 row cuts average 1018.4 elements, 0 column cuts (0 active) in 1.454 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 2 (Knapsack) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.057 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 3 (Clique) - 1 row cuts average 5.0 elements, 0 column cuts (0 active) in 0.017 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 122 row cuts average 82.9 elements, 0 column cuts (0 active) in 0.155 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 5 (FlowCover) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.024 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 6 (TwoMirCuts) - 171 row cuts average 135.5 elements, 0 column cuts (0 active) in 0.580 seconds - new frequency is 1
|
||||||
|
Cbc0001I Search completed - best objective 258618.4298960748, took 3400 iterations and 0 nodes (23.02 seconds)
|
||||||
|
Cbc0035I Maximum depth 0, 0 variables fixed on reduced cost
|
||||||
|
Cuts at root node changed objective from 239817 to 258618
|
||||||
|
Probing was tried 10 times and created 181 cuts of which 0 were active after adding rounds of cuts (2.128 seconds)
|
||||||
|
Gomory was tried 10 times and created 301 cuts of which 0 were active after adding rounds of cuts (1.454 seconds)
|
||||||
|
Knapsack was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.057 seconds)
|
||||||
|
Clique was tried 10 times and created 1 cuts of which 0 were active after adding rounds of cuts (0.017 seconds)
|
||||||
|
MixedIntegerRounding2 was tried 10 times and created 122 cuts of which 0 were active after adding rounds of cuts (0.155 seconds)
|
||||||
|
FlowCover was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.024 seconds)
|
||||||
|
TwoMirCuts was tried 10 times and created 171 cuts of which 0 were active after adding rounds of cuts (0.580 seconds)
|
||||||
|
|
||||||
|
Result - Optimal solution found
|
||||||
|
|
||||||
|
Objective value: 258618.42989607
|
||||||
|
Enumerated nodes: 0
|
||||||
|
Total iterations: 3400
|
||||||
|
Time (CPU seconds): 23.58
|
||||||
|
Time (Wallclock seconds): 23.80
|
||||||
|
|
||||||
|
Total time (CPU seconds): 25.14 (Wallclock seconds): 25.45
|
||||||
|
|
||||||
|
|
||||||
@@ -15,13 +15,17 @@ import datetime
|
|||||||
import itertools
|
import itertools
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
|
use_neos = False
|
||||||
|
|
||||||
weeks_to_rota = 26
|
weeks_to_rota = 26
|
||||||
|
|
||||||
sites = ("truro", "exeter", "torquay", "barnstaple", "plymouth")
|
sites = ("truro", "exeter", "torquay", "barnstaple", "plymouth")
|
||||||
|
|
||||||
Rota = RotaBuilder(datetime.date(2020, 9, 7),
|
Rota = RotaBuilder(datetime.date(2020, 9, 7),
|
||||||
weeks_to_rota=weeks_to_rota,
|
weeks_to_rota=weeks_to_rota,
|
||||||
balance_offset_modifier=1)
|
balance_offset_modifier=1,
|
||||||
|
max_weekend_frequency=1,
|
||||||
|
max_night_frequency=2)
|
||||||
|
|
||||||
rota_collections = {
|
rota_collections = {
|
||||||
"current": deepcopy(Rota),
|
"current": deepcopy(Rota),
|
||||||
@@ -59,27 +63,51 @@ rota_collections['current'].add_shifts(
|
|||||||
days[5:],
|
days[5:],
|
||||||
workers_required=2,
|
workers_required=2,
|
||||||
assign_as_block=True),
|
assign_as_block=True),
|
||||||
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
|
||||||
SingleShift((sites),
|
SingleShift((sites),
|
||||||
"night",
|
"night_weekday",
|
||||||
12.25,
|
12.25,
|
||||||
days,
|
days[:4],
|
||||||
balance_offset=2,
|
balance_offset=5,
|
||||||
balance_weighting=1,
|
balance_weighting=1,
|
||||||
workers_required=3,
|
workers_required=3,
|
||||||
rota_on_nwds=True),
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
SingleShift((sites),
|
||||||
|
"night_weekend",
|
||||||
|
12.25,
|
||||||
|
days[4:],
|
||||||
|
balance_offset=4,
|
||||||
|
balance_weighting=1,
|
||||||
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
rota_collections['extended proc nights only'].add_shifts(
|
rota_collections['extended proc nights only'].add_shifts(
|
||||||
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
||||||
SingleShift((sites),
|
SingleShift((sites),
|
||||||
"night",
|
"night_weekday",
|
||||||
13,
|
13,
|
||||||
days,
|
days[:4],
|
||||||
balance_offset=2,
|
balance_offset=5,
|
||||||
balance_weighting=1,
|
balance_weighting=1,
|
||||||
workers_required=3,
|
workers_required=3,
|
||||||
rota_on_nwds=True), )
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
SingleShift((sites),
|
||||||
|
"night_weekend",
|
||||||
|
13,
|
||||||
|
days[4:],
|
||||||
|
balance_offset=4,
|
||||||
|
balance_weighting=1,
|
||||||
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
)
|
||||||
|
|
||||||
rota_collections['no weekends'].add_shifts(
|
rota_collections['no weekends'].add_shifts(
|
||||||
SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:5]),
|
SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:5]),
|
||||||
@@ -88,13 +116,25 @@ rota_collections['no weekends'].add_shifts(
|
|||||||
SingleShift(("plymouth", ), "plymouth_twilight", 12.5, days[:5]),
|
SingleShift(("plymouth", ), "plymouth_twilight", 12.5, days[:5]),
|
||||||
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
||||||
SingleShift((sites),
|
SingleShift((sites),
|
||||||
"night",
|
"night_weekday",
|
||||||
13,
|
13,
|
||||||
days,
|
days[:4],
|
||||||
balance_offset=2,
|
balance_offset=5,
|
||||||
balance_weighting=1,
|
balance_weighting=1,
|
||||||
workers_required=3,
|
workers_required=3,
|
||||||
rota_on_nwds=True),
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
SingleShift((sites),
|
||||||
|
"night_weekend",
|
||||||
|
13,
|
||||||
|
days[4:],
|
||||||
|
balance_offset=4,
|
||||||
|
balance_weighting=1,
|
||||||
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
rota_collections['no twighlights'].add_shifts(
|
rota_collections['no twighlights'].add_shifts(
|
||||||
@@ -121,13 +161,25 @@ rota_collections['no twighlights'].add_shifts(
|
|||||||
assign_as_block=True),
|
assign_as_block=True),
|
||||||
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
||||||
SingleShift((sites),
|
SingleShift((sites),
|
||||||
"night",
|
"night_weekday",
|
||||||
13,
|
13,
|
||||||
days,
|
days[:4],
|
||||||
balance_offset=2,
|
balance_offset=5,
|
||||||
balance_weighting=1,
|
balance_weighting=1,
|
||||||
workers_required=3,
|
workers_required=3,
|
||||||
rota_on_nwds=True),
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
SingleShift((sites),
|
||||||
|
"night_weekend",
|
||||||
|
13,
|
||||||
|
days[4:],
|
||||||
|
balance_offset=4,
|
||||||
|
balance_weighting=1,
|
||||||
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
rota_collections['no nights'].add_shifts(
|
rota_collections['no nights'].add_shifts(
|
||||||
@@ -184,13 +236,25 @@ rota_collections['nights + proc twighlights + normal weekends'].add_shifts(
|
|||||||
assign_as_block=True),
|
assign_as_block=True),
|
||||||
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
||||||
SingleShift((sites),
|
SingleShift((sites),
|
||||||
"night",
|
"night_weekday",
|
||||||
12.25,
|
12.25,
|
||||||
days,
|
days[:4],
|
||||||
balance_offset=2,
|
balance_offset=5,
|
||||||
balance_weighting=1,
|
balance_weighting=1,
|
||||||
workers_required=3,
|
workers_required=3,
|
||||||
rota_on_nwds=True),
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
SingleShift((sites),
|
||||||
|
"night_weekend",
|
||||||
|
12.25,
|
||||||
|
days[4:],
|
||||||
|
balance_offset=4,
|
||||||
|
balance_weighting=1,
|
||||||
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
rota_collections['nights + proc twighlights + proc weekends'].add_shifts(
|
rota_collections['nights + proc twighlights + proc weekends'].add_shifts(
|
||||||
@@ -201,13 +265,25 @@ rota_collections['nights + proc twighlights + proc weekends'].add_shifts(
|
|||||||
workers_required=3),
|
workers_required=3),
|
||||||
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
#SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True),
|
||||||
SingleShift((sites),
|
SingleShift((sites),
|
||||||
"night",
|
"night_weekday",
|
||||||
12.25,
|
12.25,
|
||||||
days,
|
days[:4],
|
||||||
balance_offset=2,
|
balance_offset=5,
|
||||||
balance_weighting=1,
|
balance_weighting=1,
|
||||||
workers_required=3,
|
workers_required=3,
|
||||||
rota_on_nwds=True),
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
SingleShift((sites),
|
||||||
|
"night_weekend",
|
||||||
|
12.25,
|
||||||
|
days[4:],
|
||||||
|
balance_offset=4,
|
||||||
|
balance_weighting=1,
|
||||||
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Import trainee data
|
# Import trainee data
|
||||||
@@ -245,16 +321,30 @@ for rot in rota_collections:
|
|||||||
|
|
||||||
Rota.build_shifts_and_workers()
|
Rota.build_shifts_and_workers()
|
||||||
|
|
||||||
|
print("-Building model")
|
||||||
Rota.build_model()
|
Rota.build_model()
|
||||||
|
print("-Building model: complete")
|
||||||
|
|
||||||
opt = SolverFactory('cbc') # choose a solver
|
opt = SolverFactory('cbc') # choose a solver
|
||||||
#opt = SolverFactory('ipopt') # choose a solver
|
#opt = SolverFactory('ipopt') # choose a solver
|
||||||
|
|
||||||
|
if use_neos:
|
||||||
|
print("-Solving with neos")
|
||||||
|
solver_manager = SolverManagerFactory('neos') # Solve in neos server
|
||||||
|
results = solver_manager.solve(Rota.model,
|
||||||
|
opt=opt,
|
||||||
|
logfile="{}.log".format(rot))
|
||||||
|
else:
|
||||||
|
print("-Solving with local cbc")
|
||||||
results = opt.solve(
|
results = opt.solve(
|
||||||
Rota.model, tee=True, options={
|
Rota.model,
|
||||||
"seconds": 1200,
|
tee=True,
|
||||||
"allow": 3500
|
options={
|
||||||
}) # solve the model with the, options="seconds=60" selected solver
|
"seconds": 3200,
|
||||||
|
"allow": 2200,
|
||||||
|
},
|
||||||
|
logfile="{}.log".format(rot)
|
||||||
|
) # solve the model with the, options="seconds=60" selected solver
|
||||||
|
|
||||||
results.solver.status
|
results.solver.status
|
||||||
|
|
||||||
@@ -285,8 +375,8 @@ for rot in rota_collections:
|
|||||||
f.write("<pre>{}</pre>".format(worker_shift_summary))
|
f.write("<pre>{}</pre>".format(worker_shift_summary))
|
||||||
f.write("\n\n\n")
|
f.write("\n\n\n")
|
||||||
#workers_no_pref = ResultsHolder.get_no_preference(Rota.model.no_pref) # list with the non-satisfied workers (work on Sat but not on Sun)
|
#workers_no_pref = ResultsHolder.get_no_preference(Rota.model.no_pref) # list with the non-satisfied workers (work on Sat but not on Sun)
|
||||||
worker_night_block = ResultsHolder.get_night_blocks()
|
#worker_night_block = ResultsHolder.get_night_blocks()
|
||||||
multi = ResultsHolder.get_multinight()
|
#multi = ResultsHolder.get_multinight()
|
||||||
|
|
||||||
#break
|
#break
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class SingleShift(object):
|
|||||||
rota_on_nwds=False,
|
rota_on_nwds=False,
|
||||||
assign_as_block=False,
|
assign_as_block=False,
|
||||||
force_as_block=False,
|
force_as_block=False,
|
||||||
|
hard_constrain_shift=True,
|
||||||
constraints=[],
|
constraints=[],
|
||||||
):
|
):
|
||||||
self.site = sites
|
self.site = sites
|
||||||
@@ -51,6 +52,7 @@ class SingleShift(object):
|
|||||||
|
|
||||||
# weight the shift for balancing, default is 1
|
# weight the shift for balancing, default is 1
|
||||||
self.balance_weighting = balance_weighting
|
self.balance_weighting = balance_weighting
|
||||||
|
self.hard_constrain_shift = hard_constrain_shift
|
||||||
|
|
||||||
self.workers_required = workers_required
|
self.workers_required = workers_required
|
||||||
self.rota_on_nwds = rota_on_nwds
|
self.rota_on_nwds = rota_on_nwds
|
||||||
@@ -75,7 +77,10 @@ class RotaBuilder(object):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
start_date,
|
start_date,
|
||||||
weeks_to_rota=26,
|
weeks_to_rota=26,
|
||||||
balance_offset_modifier=1):
|
balance_offset_modifier=1,
|
||||||
|
ltft_balance_offset=4,
|
||||||
|
max_night_frequency=2,
|
||||||
|
max_weekend_frequency=2):
|
||||||
self.shifts = [] # type List[SingleShift]
|
self.shifts = [] # type List[SingleShift]
|
||||||
|
|
||||||
self.days = days
|
self.days = days
|
||||||
@@ -99,6 +104,22 @@ class RotaBuilder(object):
|
|||||||
self.sites = None
|
self.sites = None
|
||||||
|
|
||||||
self.balance_offset_modifier = balance_offset_modifier
|
self.balance_offset_modifier = balance_offset_modifier
|
||||||
|
self.ltft_balance_offset = ltft_balance_offset
|
||||||
|
|
||||||
|
# Don't assign multiple shifts every (n) weeks
|
||||||
|
self.max_night_frequency = max_night_frequency
|
||||||
|
self.max_weekend_frequency = max_weekend_frequency
|
||||||
|
|
||||||
|
self.constraint_options = {
|
||||||
|
"limit_to_1_st1_on_nights": True,
|
||||||
|
"ensure_1_st4_plus_on_nights": True,
|
||||||
|
"balance_nights": True,
|
||||||
|
"constrain_time_off_after_nights": True,
|
||||||
|
"balance_nights_across_sites": True,
|
||||||
|
"balance_blocks": True,
|
||||||
|
"balance_shifts": True,
|
||||||
|
"balance_weekends": True,
|
||||||
|
}
|
||||||
|
|
||||||
def build_model(self):
|
def build_model(self):
|
||||||
# Initialize model
|
# Initialize model
|
||||||
@@ -115,12 +136,12 @@ class RotaBuilder(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# The nights self.model is used to ensure nights are assigned as a block (and limit the number of workers required)
|
# The nights self.model is used to ensure nights are assigned as a block (and limit the number of workers required)
|
||||||
self.model.nights = Var(
|
# self.model.nights = Var(
|
||||||
((worker.id, week, block) for worker in self.workers
|
# ((worker.id, week, block) for worker in self.workers
|
||||||
for week in self.weeks for block in self.night_blocks),
|
# for week in self.weeks for block in self.night_blocks),
|
||||||
within=Binary,
|
# within=Binary,
|
||||||
initialize=0,
|
# initialize=0,
|
||||||
)
|
# )
|
||||||
|
|
||||||
# The nights self.model is used to ensure nights are assigned as a block (and limit the number of workers required)
|
# The nights self.model is used to ensure nights are assigned as a block (and limit the number of workers required)
|
||||||
self.model.shift_week_worker_assigned = Var(
|
self.model.shift_week_worker_assigned = Var(
|
||||||
@@ -156,23 +177,27 @@ class RotaBuilder(object):
|
|||||||
# Used to limit number of workers on night shift per site
|
# Used to limit number of workers on night shift per site
|
||||||
# if we force a binary it will in effect hard constrain to < 2
|
# if we force a binary it will in effect hard constrain to < 2
|
||||||
# from a single site
|
# from a single site
|
||||||
|
if self.constraint_options["balance_nights_across_sites"]:
|
||||||
self.model.night_per_site = Var(
|
self.model.night_per_site = Var(
|
||||||
((week, block, site) for site in self.sites for week in self.weeks
|
((week, shift.name, site) for site in self.sites
|
||||||
for block in ["weekday", "weekend"]),
|
for week in self.weeks
|
||||||
|
for shift in self.get_shifts_with_constraint("night")),
|
||||||
#within=Binary,
|
#within=Binary,
|
||||||
within=NonNegativeIntegers,
|
within=NonNegativeIntegers,
|
||||||
initialize=0,
|
initialize=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.model.night_per_site_t1 = Var(
|
self.model.night_per_site_t1 = Var(
|
||||||
((week, block, site) for site in self.sites for week in self.weeks
|
((week, shift.name, site) for site in self.sites
|
||||||
for block in ["weekday", "weekend"]),
|
for week in self.weeks
|
||||||
|
for shift in self.get_shifts_with_constraint("night")),
|
||||||
domain=NonNegativeIntegers,
|
domain=NonNegativeIntegers,
|
||||||
initialize=0,
|
initialize=0,
|
||||||
)
|
)
|
||||||
self.model.night_per_site_t2 = Var(
|
self.model.night_per_site_t2 = Var(
|
||||||
((week, block, site) for site in self.sites for week in self.weeks
|
((week, shift.name, site) for site in self.sites
|
||||||
for block in ["weekday", "weekend"]),
|
for week in self.weeks
|
||||||
|
for shift in self.get_shifts_with_constraint("night")),
|
||||||
domain=NonNegativeIntegers,
|
domain=NonNegativeIntegers,
|
||||||
initialize=0,
|
initialize=0,
|
||||||
)
|
)
|
||||||
@@ -196,6 +221,84 @@ class RotaBuilder(object):
|
|||||||
initialize=0,
|
initialize=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.constraint_options["balance_nights"]:
|
||||||
|
# We also try to even out the night shifts seperately
|
||||||
|
self.model.night_shift_count = Var(
|
||||||
|
((worker.id) for worker in self.workers),
|
||||||
|
domain=NonNegativeReals,
|
||||||
|
initialize=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.model.night_shift_count_t1 = Var(
|
||||||
|
((worker.id) for worker in self.workers),
|
||||||
|
domain=NonNegativeReals,
|
||||||
|
initialize=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.model.night_shift_count_t2 = Var(
|
||||||
|
((worker.id) for worker in self.workers),
|
||||||
|
domain=NonNegativeReals,
|
||||||
|
initialize=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.model.worker_weekend_assigned = Var(
|
||||||
|
((worker.id, week) for worker in self.workers
|
||||||
|
for week in self.weeks),
|
||||||
|
domain=NonNegativeReals,
|
||||||
|
initialize=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.model.works_weekend_count = Var(
|
||||||
|
((worker.id, week) for worker in self.workers
|
||||||
|
for week in self.weeks),
|
||||||
|
domain=NonNegativeIntegers,
|
||||||
|
initialize=0,
|
||||||
|
)
|
||||||
|
# self.model.works_saturday = Var(
|
||||||
|
# ((worker.id, week) for worker in self.workers
|
||||||
|
# for week in self.weeks),
|
||||||
|
# domain=Binary,
|
||||||
|
# initialize=0,
|
||||||
|
# )
|
||||||
|
# self.model.works_sunday = Var(
|
||||||
|
# ((worker.id, week) for worker in self.workers
|
||||||
|
# for week in self.weeks),
|
||||||
|
# domain=Binary,
|
||||||
|
# initialize=0,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.model.works_whole_weekend = Var(
|
||||||
|
# ((worker.id, week) for worker in self.workers
|
||||||
|
# for week in self.weeks),
|
||||||
|
# domain=Binary,
|
||||||
|
# initialize=0,
|
||||||
|
# )
|
||||||
|
|
||||||
|
self.model.works_weekend = Var(
|
||||||
|
((worker.id, week) for worker in self.workers
|
||||||
|
for week in self.weeks),
|
||||||
|
domain=Binary,
|
||||||
|
initialize=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.model.worker_weekend_count = Var(
|
||||||
|
((worker.id) for worker in self.workers),
|
||||||
|
domain=NonNegativeIntegers,
|
||||||
|
initialize=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# self.model.weekend_count_t1 = Var(
|
||||||
|
# ((worker.id) for worker in self.workers),
|
||||||
|
# domain=NonNegativeReals,
|
||||||
|
# initialize=0,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.model.weekend_count_t2 = Var(
|
||||||
|
# ((worker.id) for worker in self.workers),
|
||||||
|
# domain=NonNegativeReals,
|
||||||
|
# initialize=0,
|
||||||
|
# )
|
||||||
|
|
||||||
# self.model.unavailable = Var(((worker, week, day) for worker in self.workers for week in weeks for day in days),
|
# self.model.unavailable = Var(((worker, week, day) for worker in self.workers for week in weeks for day in days),
|
||||||
# within=Binary, initialize=0)
|
# within=Binary, initialize=0)
|
||||||
|
|
||||||
@@ -295,81 +398,57 @@ class RotaBuilder(object):
|
|||||||
self.model.max_hours_constraint = Constraint(
|
self.model.max_hours_constraint = Constraint(
|
||||||
[worker.id for worker in self.workers], rule=maxHoursRule)
|
[worker.id for worker in self.workers], rule=maxHoursRule)
|
||||||
|
|
||||||
# # Limit to 1 ST2 (or below) on a night shift
|
|
||||||
# def nightShiftMaxSTRule(model, week, block):
|
|
||||||
# """Limits to 1 ST2 (or below) per night"""
|
|
||||||
# single_workers = [w for w in self.workers if w.grade < 3]
|
|
||||||
# if not single_workers:
|
|
||||||
# return Constraint.Skip
|
|
||||||
# return sum(model.nights[w.id, week, block]
|
|
||||||
# for w in single_workers) <= 1
|
|
||||||
|
|
||||||
# # self.model.night_shifts_max_st_constraint = Constraint(
|
|
||||||
# # [week for week in self.weeks],
|
|
||||||
# # [block for block in ["weekday", "weekend"]],
|
|
||||||
# # rule=nightShiftMaxSTRule,
|
|
||||||
# # )
|
|
||||||
|
|
||||||
# Limit to 1 ST2 (or below) on a night shift
|
# Limit to 1 ST2 (or below) on a night shift
|
||||||
def nightShiftMaxSTRule_new(model, week, day, shift):
|
def nightShiftMaxSTRule(model, week, shift):
|
||||||
"""Limits to 1 ST2 (or below) per night"""
|
"""Limits to 1 ST2 (or below) per night"""
|
||||||
single_workers = [w for w in self.workers if w.grade < 3]
|
single_workers = [w for w in self.workers if w.grade < 3]
|
||||||
if not single_workers:
|
if not single_workers:
|
||||||
return Constraint.Skip
|
return Constraint.Skip
|
||||||
return sum(model.works[w.id, week, day, shift]
|
return sum(model.shift_week_worker_assigned[shift, week, w.id]
|
||||||
for w in single_workers) <= 1
|
for w in single_workers) <= 1
|
||||||
|
|
||||||
|
if self.constraint_options["limit_to_1_st1_on_nights"]:
|
||||||
self.model.night_shifts_max_st_constraint = Constraint(
|
self.model.night_shifts_max_st_constraint = Constraint(
|
||||||
[week for week in self.weeks],
|
[week for week in self.weeks],
|
||||||
[day for day in self.days],
|
[
|
||||||
[shift.name for shift in self.get_shifts_with_constraint("night")],
|
shift.name
|
||||||
rule=nightShiftMaxSTRule_new,
|
for shift in self.get_shifts_with_constraint("night")
|
||||||
|
],
|
||||||
|
rule=nightShiftMaxSTRule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enusre at least 1 ST4+ on a night shift
|
def nightShiftMinST4Rule(model, week, shift):
|
||||||
def nightShiftMinST4Rule(model, week, block):
|
|
||||||
single_workers = [w for w in self.workers if w.grade > 3]
|
single_workers = [w for w in self.workers if w.grade > 3]
|
||||||
if not single_workers:
|
if not single_workers:
|
||||||
|
print(single_workers)
|
||||||
return Constraint.Skip
|
return Constraint.Skip
|
||||||
return sum(model.nights[w.id, week, block]
|
return sum(model.shift_week_worker_assigned[shift, week, w.id]
|
||||||
for w in single_workers) >= 1
|
for w in single_workers) >= 2
|
||||||
|
|
||||||
|
if self.constraint_options["ensure_1_st4_plus_on_nights"]:
|
||||||
self.model.night_shifts_min_st4_constraint = Constraint(
|
self.model.night_shifts_min_st4_constraint = Constraint(
|
||||||
[week for week in self.weeks],
|
[week for week in self.weeks],
|
||||||
[block for block in ["weekday", "weekend"]],
|
[
|
||||||
|
shift.name
|
||||||
|
for shift in self.get_shifts_with_constraint("night")
|
||||||
|
],
|
||||||
rule=nightShiftMinST4Rule,
|
rule=nightShiftMinST4Rule,
|
||||||
)
|
)
|
||||||
|
|
||||||
# #Constraint (def of self.model.needed)
|
# # Count the number of workers from each site on each night shift
|
||||||
# for worker in self.workers:
|
# # As 1 or 0 is optimum we can simply subtract 1 from the number
|
||||||
# self.model.constraints.add(
|
if self.constraint_options["balance_nights_across_sites"]:
|
||||||
# 10000 * self.model.needed[worker.id] >= sum(
|
|
||||||
# self.model.works[worker.id, week, day, shift] for week,
|
|
||||||
# day, shift in self.get_all_shiftname_combinations())
|
|
||||||
# ) # if any self.model.works[worker, ·, ·] non-zero, self.model.needed[worker] must be one; else is zero to reduce the obj function
|
|
||||||
# #10000 is to remark, but 5 was enough since max of 40 hours yields max of 5 shifts, the maximum possible sum
|
|
||||||
|
|
||||||
# #Constraint (def of self.model.no_pref)
|
|
||||||
# for worker in self.workers:
|
|
||||||
# for week in weeks:
|
|
||||||
# self.model.constraints.add(
|
|
||||||
# self.model.no_pref[worker.id] >=
|
|
||||||
# sum(self.model.works[worker.id, week, 'Sat', shift]
|
|
||||||
# for shift in self.get_shift_names_by_day("Sat")) -
|
|
||||||
# sum(self.model.works[worker.id, week, 'Sun', shift]
|
|
||||||
# for shift in self.get_shift_names_by_day("Sun"))
|
|
||||||
# ) # if not working on sunday but working saturday self.model.needed must be 1; else will be zero to reduce the obj function
|
|
||||||
|
|
||||||
# Count the number of workers from each site on each night shift
|
|
||||||
# As 1 or 0 is optimum we can simply subtract 1 from the number
|
|
||||||
for site in self.sites:
|
for site in self.sites:
|
||||||
for block in ("weekend", "weekday"):
|
for shift in self.get_shifts_with_constraint("night"):
|
||||||
|
block = shift.name
|
||||||
for week in self.weeks:
|
for week in self.weeks:
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
self.model.night_per_site[week, block, site] == sum(
|
self.model.night_per_site[week, block, site] ==
|
||||||
self.model.nights[worker.id, week, block]
|
sum(self.model.shift_week_worker_assigned[
|
||||||
for worker in self.workers if worker.site == site)
|
block, week, worker.id]
|
||||||
#1 >= sum(self.model.nights[worker.id, week, block] for worker in self.workers if worker.site == site)
|
for worker in self.workers
|
||||||
|
if worker.site == site)
|
||||||
|
#1 >= sum(self.model.shift_week_worker_assigned[shift.name, week, worker.id] for worker in self.workers if worker.site == site)
|
||||||
)
|
)
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
self.model.night_per_site_t1[week, block, site] -
|
self.model.night_per_site_t1[week, block, site] -
|
||||||
@@ -396,7 +475,6 @@ class RotaBuilder(object):
|
|||||||
for worker in self.workers))
|
for worker in self.workers))
|
||||||
|
|
||||||
if shift.force_as_block:
|
if shift.force_as_block:
|
||||||
# Force nights to be assigned in blocks
|
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
self.model.worker_block_shift_counts[worker.id, week,
|
self.model.worker_block_shift_counts[worker.id, week,
|
||||||
shift.name] ==
|
shift.name] ==
|
||||||
@@ -412,6 +490,13 @@ class RotaBuilder(object):
|
|||||||
# Most of our constraints apply per worker
|
# Most of our constraints apply per worker
|
||||||
for worker in self.workers:
|
for worker in self.workers:
|
||||||
|
|
||||||
|
# Count number of weekends an worker works
|
||||||
|
if self.constraint_options["balance_weekends"]:
|
||||||
|
self.model.constraints.add(
|
||||||
|
self.model.worker_weekend_count[worker.id] == sum(
|
||||||
|
self.model.works_weekend[worker.id, week]
|
||||||
|
for week in self.weeks))
|
||||||
|
|
||||||
# Balance shifts within required limits (set by balance_offset)
|
# Balance shifts within required limits (set by balance_offset)
|
||||||
# If balance_offset is too restrictive (for the rota length)
|
# If balance_offset is too restrictive (for the rota length)
|
||||||
# no solution will be possible
|
# no solution will be possible
|
||||||
@@ -433,16 +518,24 @@ class RotaBuilder(object):
|
|||||||
worker.fte_adj)
|
worker.fte_adj)
|
||||||
|
|
||||||
worker.shift_target_number[shift.name] = target_shifts
|
worker.shift_target_number[shift.name] = target_shifts
|
||||||
|
if worker.name == "Jean Sukumar":
|
||||||
|
print(worker.name, shift.name, target_shifts,
|
||||||
|
total_shifts, worker.fte_adj)
|
||||||
|
|
||||||
max_shifts = target_shifts + shift.balance_offset * self.balance_offset_modifier
|
if shift.hard_constrain_shift:
|
||||||
min_shifts = target_shifts - shift.balance_offset * self.balance_offset_modifier
|
adjusted_balance_offset = shift.balance_offset
|
||||||
|
if worker.fte_adj < 100:
|
||||||
|
adjusted_balance_offset = adjusted_balance_offset * self.ltft_balance_offset
|
||||||
|
|
||||||
|
max_shifts = target_shifts + adjusted_balance_offset * self.balance_offset_modifier
|
||||||
|
min_shifts = target_shifts - adjusted_balance_offset * self.balance_offset_modifier
|
||||||
|
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
inequality(
|
inequality(
|
||||||
min_shifts,
|
min_shifts,
|
||||||
sum(self.model.works[worker.id, week, day,
|
sum(self.model.works[worker.id, week, day,
|
||||||
shift.name] for week, day in
|
shift.name] for week, day
|
||||||
self.get_week_day_combinations()),
|
in self.get_week_day_combinations()),
|
||||||
max_shifts,
|
max_shifts,
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
@@ -458,6 +551,13 @@ class RotaBuilder(object):
|
|||||||
self.model.works[worker.id, week, day, shift.name]
|
self.model.works[worker.id, week, day, shift.name]
|
||||||
for week, day in self.get_week_day_combinations()))
|
for week, day in self.get_week_day_combinations()))
|
||||||
|
|
||||||
|
if self.constraint_options["balance_nights"]:
|
||||||
|
self.model.constraints.add(
|
||||||
|
self.model.night_shift_count[worker.id] == sum(
|
||||||
|
self.model.works[worker.id, week, day, shift.name]
|
||||||
|
for week, day in self.get_week_day_combinations()
|
||||||
|
for shift in self.get_shifts_with_constraint("night")))
|
||||||
|
|
||||||
# Define shift_count_t1 and shift_count_t2 constraints for the object
|
# Define shift_count_t1 and shift_count_t2 constraints for the object
|
||||||
# Thus bypassing the need for a quadratic solver
|
# Thus bypassing the need for a quadratic solver
|
||||||
# t1-t2 is the target
|
# t1-t2 is the target
|
||||||
@@ -469,6 +569,28 @@ class RotaBuilder(object):
|
|||||||
(self.model.shift_count[worker.id, shift.name] -
|
(self.model.shift_count[worker.id, shift.name] -
|
||||||
worker.shift_target_number[shift.name]) *
|
worker.shift_target_number[shift.name]) *
|
||||||
shift.balance_weighting for shift in self.get_shifts()))
|
shift.balance_weighting for shift in self.get_shifts()))
|
||||||
|
#if "night" not in shift.constraints))
|
||||||
|
|
||||||
|
if self.constraint_options["balance_nights"]:
|
||||||
|
night_shift_target_number = sum(
|
||||||
|
worker.shift_target_number[shift.name]
|
||||||
|
for shift in self.get_shifts_with_constraint("night"))
|
||||||
|
|
||||||
|
min_shifts = night_shift_target_number - 20
|
||||||
|
max_shifts = night_shift_target_number + 20
|
||||||
|
|
||||||
|
self.model.constraints.add(
|
||||||
|
inequality(
|
||||||
|
min_shifts,
|
||||||
|
self.model.night_shift_count[worker.id],
|
||||||
|
max_shifts,
|
||||||
|
))
|
||||||
|
|
||||||
|
self.model.constraints.add(
|
||||||
|
self.model.night_shift_count_t1[worker.id] -
|
||||||
|
self.model.night_shift_count_t2[worker.id] ==
|
||||||
|
self.model.night_shift_count[worker.id] -
|
||||||
|
night_shift_target_number)
|
||||||
|
|
||||||
# Ensure worker is not allocated shifts on non working days
|
# Ensure worker is not allocated shifts on non working days
|
||||||
if worker.nwd:
|
if worker.nwd:
|
||||||
@@ -478,37 +600,74 @@ class RotaBuilder(object):
|
|||||||
0 == self.model.works[worker.id, week, day,
|
0 == self.model.works[worker.id, week, day,
|
||||||
shift.name])
|
shift.name])
|
||||||
|
|
||||||
if "night" in self.get_shift_names():
|
# for week_blocks in self.get_week_block_iterator(
|
||||||
for week_blocks in self.get_week_block_iterator(2):
|
# self.max_night_frequency):
|
||||||
# Prevent nights more than once every n weeks
|
# if self.get_shifts_with_constraint("night"):
|
||||||
self.model.constraints.add(
|
# # Prevent nights more than once every n weeks
|
||||||
1 >= sum(self.model.nights[worker.id, week, block]
|
# self.model.constraints.add(1 >= sum(
|
||||||
for week in week_blocks
|
# self.model.shift_week_worker_assigned[shift.name, week,
|
||||||
for block in ["weekday", "weekend"]))
|
# worker.id]
|
||||||
|
# for week in week_blocks
|
||||||
|
# for shift in self.get_shifts_with_constraint("night")))
|
||||||
|
|
||||||
|
# for week_blocks in self.get_week_block_iterator(
|
||||||
|
# self.max_weekend_frequency):
|
||||||
|
# # Prevent weekend shifts more than once every n weeks
|
||||||
|
# self.model.constraints.add(
|
||||||
|
# 1 >= sum(self.model.works_weekend[worker.id, week]
|
||||||
|
# for week in week_blocks))
|
||||||
|
|
||||||
for week in self.weeks:
|
for week in self.weeks:
|
||||||
|
|
||||||
|
# # model.weekend_count stores the number of weekend shifts that a worker is assigned to wor
|
||||||
|
# # this is used (by the objective) to balance the total number of weekend shifts worked
|
||||||
|
# self.model.constraints.add(
|
||||||
|
# # We could use a helper to get required shifts to check
|
||||||
|
# self.model.works_whole_weekend[worker.id, week] >= sum(
|
||||||
|
# self.model.works[worker.id, week, day, shift.name]
|
||||||
|
# for day in self.days[5:]
|
||||||
|
# for shift in self.get_shifts()) - 1)
|
||||||
|
# self.model.constraints.add(
|
||||||
|
# self.model.works_whole_weekend[worker.id, week] <= sum(
|
||||||
|
# self.model.works[worker.id, week, day, shift.name]
|
||||||
|
# for day in self.days[5:]
|
||||||
|
# for shift in self.get_shifts()) / 2)
|
||||||
|
if self.constraint_options["balance_weekends"]:
|
||||||
|
self.model.constraints.add(
|
||||||
|
self.model.works_weekend[worker.id, week] >= sum(
|
||||||
|
self.model.works[worker.id, week, day, shift.name]
|
||||||
|
for day in self.days[5:]
|
||||||
|
for shift in self.get_shifts()) / 2)
|
||||||
|
self.model.constraints.add(
|
||||||
|
self.model.works_weekend[worker.id, week] <= sum(
|
||||||
|
self.model.works[worker.id, week, day, shift.name]
|
||||||
|
for day in self.days[5:]
|
||||||
|
for shift in self.get_shifts()))
|
||||||
|
# self.model.constraints.add(
|
||||||
|
# self.model.works_saturday[worker.id, week] == sum(
|
||||||
|
# self.model.works[worker.id, week, "Sat", shift.name]
|
||||||
|
# for shift in self.get_shifts()))
|
||||||
|
# self.model.constraints.add(
|
||||||
|
# self.model.works_sunday[worker.id, week] == sum(
|
||||||
|
# self.model.works[worker.id, week, "Sun", shift.name]
|
||||||
|
# for shift in self.get_shifts()))
|
||||||
|
|
||||||
# self.model.constraints.add(1 == sum(
|
# self.model.constraints.add(1 == sum(
|
||||||
# self.model.shift_week_worker_assigned[s.name, week, worker.id]
|
# self.model.shift_week_worker_assigned[s.name, week, worker.id]
|
||||||
# for s in self.get_shifts_with_constraint("night")))
|
# for s in self.get_shifts_with_constraint("night")))
|
||||||
|
|
||||||
if "night" in self.get_shift_names():
|
for shift in self.get_shifts_with_constraint("night"):
|
||||||
# Force nights to be assigned in blocks
|
# Force nights to be assigned in blocks
|
||||||
self.model.constraints.add(1 == sum(
|
# self.model.constraints.add(8* self.model.shift_week_worker_assigned[shift.name, week, worker.id] >= sum(self.model.works[worker.id, week, day, shift.name] for day in self.days)
|
||||||
self.model.nights[worker.id, week, block]
|
# )
|
||||||
for block in self.night_blocks))
|
|
||||||
|
|
||||||
# if night block is weekday make sure Mon - Thurs is assigned as nights
|
# Force night shifts to be assigned in blocks
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
self.model.nights[worker.id, week, "weekday"] *
|
self.model.shift_week_worker_assigned[shift.name, week,
|
||||||
4 == sum(self.model.works[worker.id, week, day,
|
worker.id] *
|
||||||
"night"]
|
len(shift.shift_days) == sum(
|
||||||
for day in days[:4]))
|
self.model.works[worker.id, week, day, shift.name]
|
||||||
self.model.constraints.add(
|
for day in shift.shift_days))
|
||||||
self.model.nights[worker.id, week, "weekend"] *
|
|
||||||
3 == sum(self.model.works[worker.id, week, day,
|
|
||||||
"night"]
|
|
||||||
for day in days[4:]))
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -528,7 +687,7 @@ class RotaBuilder(object):
|
|||||||
p2week, p2day = weeks_days[n - 2]
|
p2week, p2day = weeks_days[n - 2]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
p2week, p2day = weeks_days[n]
|
p2week, p2day = weeks_days[n]
|
||||||
n2 = 0
|
p2 = 0
|
||||||
|
|
||||||
n1 = 1
|
n1 = 1
|
||||||
n2 = 1
|
n2 = 1
|
||||||
@@ -544,6 +703,7 @@ class RotaBuilder(object):
|
|||||||
n2week, n2day = weeks_days[n]
|
n2week, n2day = weeks_days[n]
|
||||||
n2 = 0
|
n2 = 0
|
||||||
|
|
||||||
|
if self.get_shift_names_by_day(day):
|
||||||
# Unable to work (hard constraint not preference)
|
# Unable to work (hard constraint not preference)
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
self.model.available[worker.id, week, day] >= sum(
|
self.model.available[worker.id, week, day] >= sum(
|
||||||
@@ -551,43 +711,50 @@ class RotaBuilder(object):
|
|||||||
for shift in self.get_shift_names_by_day(day)))
|
for shift in self.get_shift_names_by_day(day)))
|
||||||
|
|
||||||
# single shift per day
|
# single shift per day
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(1 >= sum(
|
||||||
1 >= sum(self.model.works[worker.id, week, day, shift]
|
self.model.works[worker.id, week, day, shift]
|
||||||
for shift in self.get_shift_names_by_day(day)))
|
for shift in self.get_shift_names_by_day(day)))
|
||||||
|
|
||||||
# if working a night ensure preceeding (1) or subsequent (2) shifts can only be nights
|
# if working a night ensure preceeding (1) or subsequent (2) shifts can only be nights
|
||||||
|
|
||||||
if "night" in self.get_shift_names():
|
if self.constraint_options["constrain_time_off_after_nights"]:
|
||||||
|
for constraint_shift in self.get_shifts_with_constraint(
|
||||||
|
"night"):
|
||||||
# Ensure night prior to unavalibity is not assigned
|
# Ensure night prior to unavalibity is not assigned
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
self.model.available[worker.id, week, day] >=
|
self.model.available[worker.id, week, day] >=
|
||||||
self.model.works[worker.id, pweek, pday, "night"])
|
self.model.works[worker.id, pweek, pday,
|
||||||
|
constraint_shift.name])
|
||||||
|
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
1 >= self.model.works[worker.id, week, day, "night"] +
|
1 >= self.model.works[worker.id, week, day,
|
||||||
|
constraint_shift.name] +
|
||||||
sum(n1 *
|
sum(n1 *
|
||||||
self.model.works[worker.id, nweek, nday, shift]
|
self.model.works[worker.id, nweek, nday, shift]
|
||||||
for shift in self.get_shift_names_by_day(nday)
|
for shift in self.get_shift_names_by_day(nday)
|
||||||
if shift != "night"))
|
if shift != constraint_shift.name))
|
||||||
|
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
1 >= self.model.works[worker.id, week, day, "night"] +
|
1 >= self.model.works[worker.id, week, day,
|
||||||
sum(n2 *
|
constraint_shift.name] +
|
||||||
self.model.works[worker.id, n2week, n2day, shift]
|
sum(n2 * self.model.works[worker.id, n2week, n2day,
|
||||||
|
shift]
|
||||||
for shift in self.get_shift_names_by_day(n2day)
|
for shift in self.get_shift_names_by_day(n2day)
|
||||||
if shift != "night"))
|
if shift != constraint_shift.name))
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
1 >= self.model.works[worker.id, week, day, "night"] +
|
1 >= self.model.works[worker.id, week, day,
|
||||||
|
constraint_shift.name] +
|
||||||
sum(p1 *
|
sum(p1 *
|
||||||
self.model.works[worker.id, pweek, pday, shift]
|
self.model.works[worker.id, pweek, pday, shift]
|
||||||
for shift in self.get_shift_names_by_day(pday)
|
for shift in self.get_shift_names_by_day(pday)
|
||||||
if shift != "night"))
|
if shift != constraint_shift.name))
|
||||||
self.model.constraints.add(
|
self.model.constraints.add(
|
||||||
1 >= self.model.works[worker.id, week, day, "night"] +
|
1 >= self.model.works[worker.id, week, day,
|
||||||
sum(p2 *
|
constraint_shift.name] +
|
||||||
self.model.works[worker.id, p2week, p2day, shift]
|
sum(p2 * self.model.works[worker.id, p2week, p2day,
|
||||||
|
shift]
|
||||||
for shift in self.get_shift_names_by_day(p2day)
|
for shift in self.get_shift_names_by_day(p2day)
|
||||||
if shift != "night"))
|
if shift != constraint_shift.name))
|
||||||
|
|
||||||
self.define_objectives()
|
self.define_objectives()
|
||||||
|
|
||||||
@@ -597,12 +764,25 @@ class RotaBuilder(object):
|
|||||||
|
|
||||||
# c = len(workers)
|
# c = len(workers)
|
||||||
|
|
||||||
balance_modifier_constant = 1000
|
balance_modifier_constant = 500
|
||||||
|
|
||||||
|
if self.constraint_options["balance_shifts"]:
|
||||||
shift_balancing = sum(balance_modifier_constant *
|
shift_balancing = sum(balance_modifier_constant *
|
||||||
(self.model.shift_count_t1[(worker.id)] +
|
(self.model.shift_count_t1[(worker.id)] +
|
||||||
self.model.shift_count_t2[(worker.id)])
|
self.model.shift_count_t2[(worker.id)])
|
||||||
for worker in self.workers)
|
for worker in self.workers)
|
||||||
|
else:
|
||||||
|
shift_balancing = 0
|
||||||
|
|
||||||
|
if self.constraint_options["balance_nights"]:
|
||||||
|
night_balance_modifier_constant = 2000
|
||||||
|
night_shift_balancing = sum(
|
||||||
|
night_balance_modifier_constant *
|
||||||
|
(self.model.night_shift_count_t1[(worker.id)] +
|
||||||
|
self.model.night_shift_count_t2[(worker.id)])
|
||||||
|
for worker in self.workers)
|
||||||
|
else:
|
||||||
|
night_shift_balancing = 0
|
||||||
|
|
||||||
preference_constant = 10
|
preference_constant = 10
|
||||||
# Preferences
|
# Preferences
|
||||||
@@ -614,19 +794,29 @@ class RotaBuilder(object):
|
|||||||
|
|
||||||
# # Spread nights
|
# # Spread nights
|
||||||
|
|
||||||
nights_balancing = sum(
|
if self.constraint_options["balance_nights_across_sites"]:
|
||||||
self.model.night_per_site_t1[week, block, site] * 20
|
nights_site_balancing = sum(
|
||||||
|
(self.model.night_per_site_t1[week, shift.name, site] +
|
||||||
|
self.model.night_per_site_t2[week, shift.name, site]) * 20
|
||||||
#self.model.night_per_site2[week, block, site]
|
#self.model.night_per_site2[week, block, site]
|
||||||
for week in self.weeks for block in ("weekday", "weekend")
|
for week in self.weeks
|
||||||
|
for shift in self.get_shifts_with_constraint("night")
|
||||||
for site in self.sites)
|
for site in self.sites)
|
||||||
#nights_balancing = 0
|
else:
|
||||||
|
nights_site_balancing = 0
|
||||||
|
|
||||||
|
if self.constraint_options["balance_blocks"]:
|
||||||
blocks_balancing = sum(
|
blocks_balancing = sum(
|
||||||
100 * self.model.blocks_assigned[week, shift]
|
100 * self.model.blocks_assigned[week, shift]
|
||||||
for week in self.weeks
|
for week in self.weeks
|
||||||
for shift in self.shifts_to_assign_as_blocks())
|
for shift in self.shifts_to_assign_as_blocks())
|
||||||
|
else:
|
||||||
|
blocks_balancing = 0
|
||||||
|
|
||||||
return shift_balancing + preferences + nights_balancing + blocks_balancing
|
return night_shift_balancing
|
||||||
|
#return shift_balancing + preferences + blocks_balancing
|
||||||
|
#return shift_balancing + preferences + nights_site_balancing + blocks_balancing
|
||||||
|
return shift_balancing + night_shift_balancing + preferences + nights_site_balancing + blocks_balancing
|
||||||
|
|
||||||
# add objective function to the model. rule (pass function) or expr (pass expression directly)
|
# add objective function to the model. rule (pass function) or expr (pass expression directly)
|
||||||
self.model.obj = Objective(rule=obj_rule, sense=minimize)
|
self.model.obj = Objective(rule=obj_rule, sense=minimize)
|
||||||
@@ -874,6 +1064,9 @@ class RotaBuilder(object):
|
|||||||
s.extend(self.shifts_to_force_as_blocks())
|
s.extend(self.shifts_to_force_as_blocks())
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
def get_workers_total_fte(self):
|
||||||
|
return self.full_time_equivalent
|
||||||
|
|
||||||
|
|
||||||
class RotaResults(object):
|
class RotaResults(object):
|
||||||
def __init__(self, rota, results):
|
def __init__(self, rota, results):
|
||||||
@@ -946,7 +1139,10 @@ class RotaResults(object):
|
|||||||
timetable[worker.name][week][day] = shift
|
timetable[worker.name][week][day] = shift
|
||||||
return timetable
|
return timetable
|
||||||
|
|
||||||
def get_worker_timetable_brief(self, show_prefs=True, marker_every=19):
|
def get_worker_timetable_brief(self,
|
||||||
|
show_prefs=True,
|
||||||
|
marker_every=19,
|
||||||
|
show_unavailable=True):
|
||||||
model = self.rota.model
|
model = self.rota.model
|
||||||
week_string = "{:20}".format("-Week-") + "".join(
|
week_string = "{:20}".format("-Week-") + "".join(
|
||||||
[7 * str("{}".format(w))[-1:] for w in self.rota.weeks])
|
[7 * str("{}".format(w))[-1:] for w in self.rota.weeks])
|
||||||
@@ -968,6 +1164,9 @@ class RotaResults(object):
|
|||||||
for s in set(shifts):
|
for s in set(shifts):
|
||||||
shift_count = shift_count + "{}: {}, ".format(
|
shift_count = shift_count + "{}: {}, ".format(
|
||||||
s, shifts.count(s))
|
s, shifts.count(s))
|
||||||
|
|
||||||
|
shift_count = shift_count + "#weekends_worked: {}\\#".format(
|
||||||
|
model.worker_weekend_count[worker.id].value)
|
||||||
timetable.append("{} {}".format("".join(w), shift_count))
|
timetable.append("{} {}".format("".join(w), shift_count))
|
||||||
|
|
||||||
if show_prefs:
|
if show_prefs:
|
||||||
@@ -980,6 +1179,16 @@ class RotaResults(object):
|
|||||||
w.append("N")
|
w.append("N")
|
||||||
timetable.append("".join(w))
|
timetable.append("".join(w))
|
||||||
|
|
||||||
|
if show_unavailable:
|
||||||
|
# prefs
|
||||||
|
w = ["{:20}".format("Unavailable")]
|
||||||
|
for week, day in self.rota.get_week_day_combinations():
|
||||||
|
if model.available[worker.id, week, day] > 0:
|
||||||
|
w.append("A")
|
||||||
|
else:
|
||||||
|
w.append("U")
|
||||||
|
timetable.append("".join(w))
|
||||||
|
|
||||||
i = marker_every
|
i = marker_every
|
||||||
while i < len(timetable):
|
while i < len(timetable):
|
||||||
timetable.insert(i, week_string)
|
timetable.insert(i, week_string)
|
||||||
@@ -1024,26 +1233,27 @@ class RotaResults(object):
|
|||||||
# """Extract to a list the workers not satisfied with their weekend preference."""
|
# """Extract to a list the workers not satisfied with their weekend preference."""
|
||||||
# return [worker.id for worker in workers if no_pref[worker.id].value == 1]
|
# return [worker.id for worker in workers if no_pref[worker.id].value == 1]
|
||||||
|
|
||||||
def get_night_blocks(self):
|
# def get_night_blocks(self):
|
||||||
nights = self.rota.model.nights
|
# nights = self.rota.model.nights
|
||||||
timetable = {
|
# timetable = {
|
||||||
worker.name: {
|
# worker.name: {
|
||||||
week: {block: ""
|
# week: {block: ""
|
||||||
for block in self.rota.night_blocks}
|
# for block in self.rota.night_blocks}
|
||||||
for week in self.rota.weeks
|
# for week in self.rota.weeks
|
||||||
}
|
# }
|
||||||
for worker in self.rota.workers
|
# for worker in self.rota.workers
|
||||||
}
|
# }
|
||||||
for worker in self.rota.workers:
|
# for worker in self.rota.workers:
|
||||||
for week in self.rota.weeks:
|
# for week in self.rota.weeks:
|
||||||
for block in self.rota.night_blocks:
|
# for block in self.rota.night_blocks:
|
||||||
if nights[worker.id, week, block].value == 1:
|
# if nights[worker.id, week, block].value == 1:
|
||||||
timetable[worker.name][week][block] = "true"
|
# timetable[worker.name][week][block] = "true"
|
||||||
return timetable
|
# return timetable
|
||||||
|
|
||||||
def get_multinight(self):
|
def get_multinight(self):
|
||||||
for site in self.rota.sites:
|
for site in self.rota.sites:
|
||||||
for block in ("weekend", "weekday"):
|
for shift in self.get_shifts_with_constraint("night"):
|
||||||
|
block = shift.name
|
||||||
for week in self.rota.weeks:
|
for week in self.rota.weeks:
|
||||||
print(
|
print(
|
||||||
site, week, block,
|
site, week, block,
|
||||||
@@ -1053,3 +1263,13 @@ class RotaResults(object):
|
|||||||
site].value,
|
site].value,
|
||||||
self.rota.model.night_per_site_t2[week, block,
|
self.rota.model.night_per_site_t2[week, block,
|
||||||
site].value)
|
site].value)
|
||||||
|
|
||||||
|
def get_night_details(self):
|
||||||
|
for worker in self.rota.workers:
|
||||||
|
print(
|
||||||
|
worker.name,
|
||||||
|
self.rota.model.night_shift_count[(worker.id)].value,
|
||||||
|
self.rota.model.night_shift_count_t1[(worker.id)].value,
|
||||||
|
self.rota.model.night_shift_count_t2[(worker.id)].value,
|
||||||
|
worker.shift_target_number["night_weekday"] +
|
||||||
|
worker.shift_target_number["night_weekend"])
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
Solver command line: ['/usr/bin/cbc', '-seconds', '1200', '-allow', '4000', '-printingOptions', 'all', '-import', '/tmp/tmpeun6jpa7.pyomo.lp', '-stat=1', '-solve', '-solu', '/tmp/tmpeun6jpa7.pyomo.soln']
|
||||||
|
|
||||||
|
Welcome to the CBC MILP Solver
|
||||||
|
Version: 2.9.9
|
||||||
|
Build Date: Aug 7 2019
|
||||||
|
|
||||||
|
command line - /usr/bin/cbc -seconds 1200 -allow 4000 -printingOptions all -import /tmp/tmpeun6jpa7.pyomo.lp -stat=1 -solve -solu /tmp/tmpeun6jpa7.pyomo.soln (default strategy 1)
|
||||||
|
seconds was changed from 1e+100 to 1200
|
||||||
|
allowableGap was changed from 1e-10 to 4000
|
||||||
|
Option for printingOptions changed from normal to all
|
||||||
|
Presolve 57035 (-82138) rows, 23862 (-89251) columns and 226642 (-883011) elements
|
||||||
|
Statistics for presolved model
|
||||||
|
Original problem has 112272 integers (112112 of which binary)
|
||||||
|
Presolved problem has 23696 integers (23696 of which binary)
|
||||||
|
==== 23751 zero objective 2 different
|
||||||
|
23751 variables have objective of 0
|
||||||
|
111 variables have objective of 2000
|
||||||
|
==== absolute objective values 2 different
|
||||||
|
23751 variables have objective of 0
|
||||||
|
111 variables have objective of 2000
|
||||||
|
==== for integers 23696 zero objective 1 different
|
||||||
|
23696 variables have objective of 0
|
||||||
|
==== for integers absolute objective values 1 different
|
||||||
|
23696 variables have objective of 0
|
||||||
|
===== end objective counts
|
||||||
|
|
||||||
|
|
||||||
|
Problem has 57035 rows, 23862 columns (111 with objective) and 226642 elements
|
||||||
|
There are 111 singletons with objective 988 singletons with no objective
|
||||||
|
Column breakdown:
|
||||||
|
110 of type 0.0->inf, 56 of type 0.0->up, 0 of type lo->inf,
|
||||||
|
0 of type lo->up, 0 of type free, 0 of type fixed,
|
||||||
|
0 of type -inf->0.0, 0 of type -inf->up, 23696 of type 0.0->1.0
|
||||||
|
Row breakdown:
|
||||||
|
2843 of type E 0.0, 676 of type E 1.0, 0 of type E -1.0,
|
||||||
|
289 of type E other, 0 of type G 0.0, 0 of type G 1.0,
|
||||||
|
231 of type G other, 2788 of type L 0.0, 48596 of type L 1.0,
|
||||||
|
1612 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other,
|
||||||
|
0 of type Free
|
||||||
|
Continuous objective value is 497995 - 22.34 seconds
|
||||||
|
Cgl0002I 82402 variables fixed
|
||||||
|
Cgl0003I 0 fixed, 55 tightened bounds, 34225 strengthened rows, 50251 substitutions
|
||||||
|
Cgl0004I processed model has 17402 rows, 14104 columns (13939 integer (13939 of which binary)) and 89454 elements
|
||||||
|
Cbc0038I Initial state - 511 integers unsatisfied sum - 146.075
|
||||||
|
Cbc0038I Pass 1: (33.84 seconds) suminf. 63.26415 (238) obj. 530335 iterations 2863
|
||||||
|
Cbc0038I Pass 2: (33.87 seconds) suminf. 52.41969 (214) obj. 526335 iterations 226
|
||||||
|
Cbc0038I Pass 3: (33.90 seconds) suminf. 51.56248 (211) obj. 526335 iterations 61
|
||||||
|
Cbc0038I Pass 4: (33.93 seconds) suminf. 51.15178 (211) obj. 526335 iterations 79
|
||||||
|
Cbc0038I Pass 5: (33.96 seconds) suminf. 45.85375 (210) obj. 529354 iterations 94
|
||||||
|
Cbc0038I Pass 6: (33.99 seconds) suminf. 45.71180 (212) obj. 529354 iterations 41
|
||||||
|
Cbc0038I Pass 7: (34.04 seconds) suminf. 35.87037 (187) obj. 529354 iterations 322
|
||||||
|
Cbc0038I Pass 8: (34.08 seconds) suminf. 33.85554 (195) obj. 526761 iterations 264
|
||||||
|
Cbc0038I Pass 9: (34.11 seconds) suminf. 33.14549 (176) obj. 526830 iterations 105
|
||||||
|
Cbc0038I Pass 10: (34.14 seconds) suminf. 31.13224 (172) obj. 526830 iterations 115
|
||||||
|
Cbc0038I Pass 11: (34.21 seconds) suminf. 30.55620 (160) obj. 529691 iterations 562
|
||||||
|
Cbc0038I Pass 12: (34.25 seconds) suminf. 28.64178 (153) obj. 534012 iterations 205
|
||||||
|
Cbc0038I Pass 13: (34.29 seconds) suminf. 29.16979 (146) obj. 527774 iterations 240
|
||||||
|
Cbc0038I Pass 14: (34.31 seconds) suminf. 28.35432 (144) obj. 523774 iterations 4
|
||||||
|
Cbc0038I Pass 15: (34.37 seconds) suminf. 25.43149 (140) obj. 525658 iterations 341
|
||||||
|
Cbc0038I Pass 16: (34.39 seconds) suminf. 25.05606 (138) obj. 525658 iterations 13
|
||||||
|
Cbc0038I Pass 17: (34.42 seconds) suminf. 25.05606 (133) obj. 525988 iterations 132
|
||||||
|
Cbc0038I Pass 18: (34.45 seconds) suminf. 25.05606 (133) obj. 525988 iterations 6
|
||||||
|
Cbc0038I Pass 19: (34.52 seconds) suminf. 25.01303 (143) obj. 526804 iterations 492
|
||||||
|
Cbc0038I Pass 20: (34.55 seconds) suminf. 24.96852 (141) obj. 526288 iterations 33
|
||||||
|
Cbc0038I Pass 21: (34.63 seconds) suminf. 22.73642 (133) obj. 527464 iterations 691
|
||||||
|
Cbc0038I Pass 22: (34.64 seconds) suminf. 22.73642 (133) obj. 527464 iterations 0
|
||||||
|
Cbc0038I Pass 23: (34.69 seconds) suminf. 22.27803 (129) obj. 527464 iterations 346
|
||||||
|
Cbc0038I Pass 24: (34.72 seconds) suminf. 22.01961 (123) obj. 527464 iterations 76
|
||||||
|
Cbc0038I Pass 25: (34.79 seconds) suminf. 22.04026 (123) obj. 526572 iterations 541
|
||||||
|
Cbc0038I Pass 26: (34.82 seconds) suminf. 22.04026 (121) obj. 526572 iterations 47
|
||||||
|
Cbc0038I Pass 27: (34.86 seconds) suminf. 22.15035 (111) obj. 527464 iterations 339
|
||||||
|
Cbc0038I Pass 28: (34.89 seconds) suminf. 22.04689 (111) obj. 527464 iterations 91
|
||||||
|
Cbc0038I Pass 29: (34.95 seconds) suminf. 22.01936 (123) obj. 526475 iterations 455
|
||||||
|
Cbc0038I Pass 30: (34.98 seconds) suminf. 21.93681 (123) obj. 526475 iterations 40
|
||||||
|
Cbc0038I No solution found this major pass
|
||||||
|
Cbc0038I Before mini branch and bound, 13026 integers at bound fixed and 50 continuous
|
||||||
|
Cbc0038I Full problem 17402 rows 14104 columns, reduced to 796 rows 583 columns
|
||||||
|
Cbc0038I Mini branch and bound did not improve solution (35.07 seconds)
|
||||||
|
Cbc0038I After 37.37 seconds - Feasibility pump exiting - took 3.92 seconds
|
||||||
|
Cbc0031I 166 added rows had average density of 73.783133
|
||||||
|
Cbc0013I At root node, 166 cuts changed objective from 497995.24 to 497995.24 in 10 passes
|
||||||
|
Cbc0014I Cut generator 0 (Probing) - 298 row cuts average 8.2 elements, 0 column cuts (123 active) in 1.134 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 1 (Gomory) - 985 row cuts average 585.6 elements, 0 column cuts (0 active) in 0.785 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 2 (Knapsack) - 170 row cuts average 2.9 elements, 0 column cuts (0 active) in 0.100 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.012 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 285 row cuts average 68.8 elements, 0 column cuts (0 active) in 0.137 seconds - new frequency is 1
|
||||||
|
Cbc0014I Cut generator 5 (FlowCover) - 26 row cuts average 28.7 elements, 0 column cuts (0 active) in 0.019 seconds - new frequency is -100
|
||||||
|
Cbc0014I Cut generator 6 (TwoMirCuts) - 723 row cuts average 138.8 elements, 0 column cuts (0 active) in 0.677 seconds - new frequency is -100
|
||||||
|
Cbc0010I After 0 nodes, 1 on tree, 1e+50 best solution, best possible 497995.24 (43.62 seconds)
|
||||||
|
Cbc0010I After 100 nodes, 57 on tree, 1e+50 best solution, best possible 497995.24 (59.88 seconds)
|
||||||
|
Cbc0010I After 200 nodes, 109 on tree, 1e+50 best solution, best possible 497995.24 (64.07 seconds)
|
||||||
|
Cbc0010I After 300 nodes, 161 on tree, 1e+50 best solution, best possible 497995.24 (68.27 seconds)
|
||||||
|
Cbc0012I Integer solution of 497995.24 found by rounding after 15921 iterations and 379 nodes (71.58 seconds)
|
||||||
|
Cbc0038I Full problem 17402 rows 14104 columns, reduced to 0 rows 0 columns
|
||||||
|
Cbc0001I Search completed - best objective 497995.2442909057, took 15921 iterations and 379 nodes (71.87 seconds)
|
||||||
|
Cbc0032I Strong branching done 2148 times (28125 iterations), fathomed 0 nodes and fixed 0 variables
|
||||||
|
Cbc0035I Maximum depth 100, 0 variables fixed on reduced cost
|
||||||
|
Cuts at root node changed objective from 497995 to 497995
|
||||||
|
Probing was tried 119 times and created 537 cuts of which 123 were active after adding rounds of cuts (1.734 seconds)
|
||||||
|
Gomory was tried 119 times and created 1156 cuts of which 0 were active after adding rounds of cuts (1.679 seconds)
|
||||||
|
Knapsack was tried 119 times and created 219 cuts of which 0 were active after adding rounds of cuts (1.038 seconds)
|
||||||
|
Clique was tried 10 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.012 seconds)
|
||||||
|
MixedIntegerRounding2 was tried 119 times and created 374 cuts of which 0 were active after adding rounds of cuts (1.404 seconds)
|
||||||
|
FlowCover was tried 10 times and created 26 cuts of which 0 were active after adding rounds of cuts (0.019 seconds)
|
||||||
|
TwoMirCuts was tried 10 times and created 723 cuts of which 0 were active after adding rounds of cuts (0.677 seconds)
|
||||||
|
ImplicationCuts was tried 15 times and created 16 cuts of which 0 were active after adding rounds of cuts (0.023 seconds)
|
||||||
|
|
||||||
|
Result - Optimal solution found
|
||||||
|
|
||||||
|
Objective value: 497995.24429091
|
||||||
|
Enumerated nodes: 379
|
||||||
|
Total iterations: 15921
|
||||||
|
Time (CPU seconds): 72.51
|
||||||
|
Time (Wallclock seconds): 72.80
|
||||||
|
|
||||||
|
Total time (CPU seconds): 74.98 (Wallclock seconds): 75.41
|
||||||
|
|
||||||
|
|
||||||
@@ -15,56 +15,120 @@ import datetime
|
|||||||
import itertools
|
import itertools
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
weeks_to_rota = 16
|
use_neos = False
|
||||||
|
|
||||||
|
weeks_to_rota = 26
|
||||||
|
|
||||||
sites = ("truro", "exeter", "torquay", "barnstaple", "plymouth")
|
sites = ("truro", "exeter", "torquay", "barnstaple", "plymouth")
|
||||||
|
|
||||||
Rota = RotaBuilder(datetime.date(2020, 9, 7),
|
Rota = RotaBuilder(datetime.date(2020, 9, 7),
|
||||||
weeks_to_rota=weeks_to_rota,
|
weeks_to_rota=weeks_to_rota,
|
||||||
balance_offset_modifier=1)
|
balance_offset_modifier=1,
|
||||||
|
max_weekend_frequency=1,
|
||||||
|
max_night_frequency=1)
|
||||||
|
|
||||||
Rota.add_shifts(
|
Rota.add_shifts(
|
||||||
|
# SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:5]),
|
||||||
|
# # SingleShift(("truro", ),
|
||||||
|
# # "truro_twilight",
|
||||||
|
# # 12.5,
|
||||||
|
# # days[:5],
|
||||||
|
# # assign_as_block=False),
|
||||||
|
# #SingleShift(("torquay", ), "torquay_twilight", 12.5, days[:5]),
|
||||||
|
# #SingleShift(("plymouth", ), "plymouth_twilight", 12.5, days[:5]),
|
||||||
|
# SingleShift(("exeter", ),
|
||||||
|
# "weekend_exeter",
|
||||||
|
# 12.5,
|
||||||
|
# days[5:],
|
||||||
|
# assign_as_block=False),
|
||||||
|
# SingleShift(("truro", ),
|
||||||
|
# "weekend_truro",
|
||||||
|
# 12.5,
|
||||||
|
# days[5:],
|
||||||
|
# force_as_block=True),
|
||||||
|
# # SingleShift(("torquay", ),
|
||||||
|
# # "weekend_torquay",
|
||||||
|
# # 12.5,
|
||||||
|
# # days[5:],
|
||||||
|
# # assign_as_block=True),
|
||||||
|
# # SingleShift(("plymouth", ),
|
||||||
|
# # "weekend_plymouth",
|
||||||
|
# # 8,
|
||||||
|
# # days[5:],
|
||||||
|
# # workers_required=2,
|
||||||
|
# # assign_as_block=True),
|
||||||
|
# SingleShift((sites),
|
||||||
|
# "night_weekday",
|
||||||
|
# 12.25,
|
||||||
|
# days[:4],
|
||||||
|
# balance_offset=2,
|
||||||
|
# balance_weighting=1,
|
||||||
|
# workers_required=3,
|
||||||
|
# force_as_block=False,
|
||||||
|
# rota_on_nwds=True,
|
||||||
|
# constraints=["night"]),
|
||||||
|
# SingleShift((sites),
|
||||||
|
# "night_weekend",
|
||||||
|
# 12.25,
|
||||||
|
# days[4:],
|
||||||
|
# balance_offset=2,
|
||||||
|
# balance_weighting=1,
|
||||||
|
# workers_required=3,
|
||||||
|
# force_as_block=False,
|
||||||
|
# rota_on_nwds=True,
|
||||||
|
# constraints=["night"]),
|
||||||
SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:5]),
|
SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:5]),
|
||||||
SingleShift(("truro", ),
|
SingleShift(("truro", ), "truro_twilight", 12.5, days[:5]),
|
||||||
"truro_twilight",
|
SingleShift(("torquay", ), "torquay_twilight", 12.5, days[:5]),
|
||||||
12.5,
|
SingleShift(("plymouth", ), "plymouth_twilight", 12.5, days[:5]),
|
||||||
days[:5],
|
|
||||||
assign_as_block=False),
|
|
||||||
#SingleShift(("torquay", ), "torquay_twilight", 12.5, days[:5]),
|
|
||||||
#SingleShift(("plymouth", ), "plymouth_twilight", 12.5, days[:5]),
|
|
||||||
SingleShift(("exeter", ),
|
SingleShift(("exeter", ),
|
||||||
"weekend_exeter",
|
"weekend_exeter",
|
||||||
12.5,
|
12.5,
|
||||||
days[5:],
|
days[5:],
|
||||||
force_as_block=True),
|
assign_as_block=True),
|
||||||
SingleShift(("truro", ),
|
SingleShift(("truro", ),
|
||||||
"weekend_truro",
|
"weekend_truro",
|
||||||
12.5,
|
12.5,
|
||||||
days[5:],
|
days[5:],
|
||||||
assign_as_block=True),
|
assign_as_block=True),
|
||||||
# SingleShift(("torquay", ),
|
SingleShift(("torquay", ),
|
||||||
# "weekend_torquay",
|
"weekend_torquay",
|
||||||
# 12.5,
|
12.5,
|
||||||
# days[5:],
|
days[5:],
|
||||||
# assign_as_block=True),
|
assign_as_block=True),
|
||||||
# SingleShift(("plymouth", ),
|
SingleShift(("plymouth", ),
|
||||||
# "weekend_plymouth",
|
"weekend_plymouth",
|
||||||
# 8,
|
8,
|
||||||
# days[5:],
|
days[5:],
|
||||||
# workers_required=2,
|
workers_required=2,
|
||||||
# assign_as_block=True),
|
assign_as_block=True),
|
||||||
SingleShift((sites),
|
SingleShift(
|
||||||
"night",
|
(sites),
|
||||||
|
"night_weekday",
|
||||||
12.25,
|
12.25,
|
||||||
days,
|
days[:4],
|
||||||
balance_offset=2,
|
balance_offset=4,
|
||||||
balance_weighting=1,
|
balance_weighting=1,
|
||||||
|
#hard_constrain_shift=False,
|
||||||
workers_required=3,
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
|
rota_on_nwds=True,
|
||||||
|
constraints=["night"]),
|
||||||
|
SingleShift(
|
||||||
|
(sites),
|
||||||
|
"night_weekend",
|
||||||
|
12.25,
|
||||||
|
days[4:],
|
||||||
|
balance_offset=3,
|
||||||
|
balance_weighting=1,
|
||||||
|
#hard_constrain_shift=False,
|
||||||
|
workers_required=3,
|
||||||
|
force_as_block=False,
|
||||||
rota_on_nwds=True,
|
rota_on_nwds=True,
|
||||||
constraints=["night"]),
|
constraints=["night"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
use_test_workers = True
|
use_test_workers = False
|
||||||
|
|
||||||
# Import trainee data
|
# Import trainee data
|
||||||
if use_test_workers:
|
if use_test_workers:
|
||||||
@@ -83,29 +147,35 @@ if use_test_workers:
|
|||||||
"Truro {}".format(i),
|
"Truro {}".format(i),
|
||||||
"truro",
|
"truro",
|
||||||
4,
|
4,
|
||||||
) for i in range(2, 14)
|
) for i in range(1, 9)
|
||||||
|
])
|
||||||
|
Rota.add_workers([
|
||||||
|
Worker(Rota, i, "Plym {}".format(i), "plymouth", 4, 50)
|
||||||
|
for i in range(9, 17)
|
||||||
|
])
|
||||||
|
Rota.add_workers([
|
||||||
|
Worker(Rota, i, "Plym {}".format(i), "plymouth", 4, 80)
|
||||||
|
for i in range(17, 19)
|
||||||
])
|
])
|
||||||
# Rota.add_workers([
|
# Rota.add_workers([
|
||||||
# Worker(Rota, i, "Plym {}".format(i), "plymouth", 4)
|
# Worker(
|
||||||
# for i in range(16, 30)
|
# Rota,
|
||||||
|
# i,
|
||||||
|
# "Exe {}".format(i),
|
||||||
|
# "exeter",
|
||||||
|
# 4,
|
||||||
|
# ) for i in range(20, 25)
|
||||||
|
# ])
|
||||||
|
# Rota.add_workers([
|
||||||
|
# Worker(
|
||||||
|
# Rota,
|
||||||
|
# i,
|
||||||
|
# "Torq {}".format(i),
|
||||||
|
# "torquay",
|
||||||
|
# 2,
|
||||||
|
# 50,
|
||||||
|
# ) for i in range(25, 29)
|
||||||
# ])
|
# ])
|
||||||
Rota.add_workers([
|
|
||||||
Worker(
|
|
||||||
Rota,
|
|
||||||
i,
|
|
||||||
"Exe {}".format(i),
|
|
||||||
"exeter",
|
|
||||||
4,
|
|
||||||
) for i in range(30, 40)
|
|
||||||
])
|
|
||||||
Rota.add_workers([
|
|
||||||
Worker(Rota,
|
|
||||||
i,
|
|
||||||
"Torq {}".format(i),
|
|
||||||
"torquay",
|
|
||||||
2,
|
|
||||||
pref_not_to_work=(("2020/09/07"), )) for i in range(50, 60)
|
|
||||||
])
|
|
||||||
else:
|
else:
|
||||||
import csv
|
import csv
|
||||||
with open('trainees.csv', newline='') as f:
|
with open('trainees.csv', newline='') as f:
|
||||||
@@ -119,7 +189,7 @@ else:
|
|||||||
name, site, grade, fte, nwd, end_date, oop, extra = row
|
name, site, grade, fte, nwd, end_date, oop, extra = row
|
||||||
|
|
||||||
# Ignore trainees if fte == 0 (what are they doing here anyway)
|
# Ignore trainees if fte == 0 (what are they doing here anyway)
|
||||||
if fte == 0:
|
if int(fte) < 1:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
nwds = nwd.split(",").title() if nwd else None
|
nwds = nwd.split(",").title() if nwd else None
|
||||||
@@ -130,6 +200,13 @@ else:
|
|||||||
Worker(Rota, n, name, site.lower(), int(grade[2]), int(fte),
|
Worker(Rota, n, name, site.lower(), int(grade[2]), int(fte),
|
||||||
nwds, end_date, oop))
|
nwds, end_date, oop))
|
||||||
|
|
||||||
|
Rota.constraint_options["limit_to_1_st1_on_nights"] = True
|
||||||
|
Rota.constraint_options["balance_nights"] = True
|
||||||
|
Rota.constraint_options["constrain_time_off_after_nights"] = True
|
||||||
|
Rota.constraint_options["balance_nights_across_sites"] = False
|
||||||
|
Rota.constraint_options["balance_blocks"] = False
|
||||||
|
Rota.constraint_options["balance_weekends"] = True
|
||||||
|
|
||||||
print(0)
|
print(0)
|
||||||
Rota.build_shifts_and_workers()
|
Rota.build_shifts_and_workers()
|
||||||
|
|
||||||
@@ -140,11 +217,19 @@ opt = SolverFactory('cbc') # choose a solver
|
|||||||
#opt = SolverFactory('ipopt') # choose a solver
|
#opt = SolverFactory('ipopt') # choose a solver
|
||||||
|
|
||||||
print(2)
|
print(2)
|
||||||
|
if use_neos:
|
||||||
|
solver_manager = SolverManagerFactory('neos') # Solve in neos server
|
||||||
|
results = solver_manager.solve(Rota.model, opt=opt, logfile="test.log")
|
||||||
|
else:
|
||||||
results = opt.solve(
|
results = opt.solve(
|
||||||
Rota.model, tee=True, options={
|
Rota.model,
|
||||||
|
tee=True,
|
||||||
|
options={
|
||||||
"seconds": 1200,
|
"seconds": 1200,
|
||||||
"allow": 70000
|
"allow": 4000,
|
||||||
}) # solve the model with the, options="seconds=60" selected solver
|
},
|
||||||
|
logfile="test.log"
|
||||||
|
) # solve the model with the, options="seconds=60" selected solver
|
||||||
|
|
||||||
print(results)
|
print(results)
|
||||||
results.solver.status
|
results.solver.status
|
||||||
@@ -156,9 +241,12 @@ ResultsHolder = RotaResults(Rota, results)
|
|||||||
week_table = ResultsHolder.get_work_table() # list with the required workers
|
week_table = ResultsHolder.get_work_table() # list with the required workers
|
||||||
worker_timetable = ResultsHolder.get_worker_timetable()
|
worker_timetable = ResultsHolder.get_worker_timetable()
|
||||||
worker_timetable_brief = ResultsHolder.get_worker_timetable_brief(
|
worker_timetable_brief = ResultsHolder.get_worker_timetable_brief(
|
||||||
show_prefs=False)
|
show_prefs=False, show_unavailable=False)
|
||||||
|
|
||||||
print(worker_timetable_brief)
|
print(worker_timetable_brief)
|
||||||
|
print(Rota.get_workers_total_fte())
|
||||||
|
|
||||||
|
ResultsHolder.get_night_details()
|
||||||
|
|
||||||
# for i in ResultsHolder.rota.model.blocks_worker_shift_count:
|
# for i in ResultsHolder.rota.model.blocks_worker_shift_count:
|
||||||
# n = ResultsHolder.rota.model.blocks_worker_shift_count[i].value
|
# n = ResultsHolder.rota.model.blocks_worker_shift_count[i].value
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class Worker:
|
|||||||
end_oop_date = datetime.datetime.strptime(end_oop,
|
end_oop_date = datetime.datetime.strptime(end_oop,
|
||||||
"%Y/%m/%d").date()
|
"%Y/%m/%d").date()
|
||||||
|
|
||||||
|
print(start_oop_date, end_oop_date)
|
||||||
if end_oop_date > Rota.rota_end_date:
|
if end_oop_date > Rota.rota_end_date:
|
||||||
end_oop_date = Rota.rota_end_date
|
end_oop_date = Rota.rota_end_date
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user