diff --git a/Solvers-Results.txt b/Solvers-Results.txt new file mode 100644 index 0000000..f1f95ad --- /dev/null +++ b/Solvers-Results.txt @@ -0,0 +1,144 @@ + basic random +instance size best runtime makespan ecart runtime makespan ecart +aaa1 2x3 11 1 12 9.1 999 11 0.0 +ft06 6x6 55 0 60 9.1 999 55 0.0 +ft10 10x10 930 0 1319 41.8 999 1209 30.0 +ft20 20x5 1165 0 1672 43.5 999 1529 31.2 +la01 10x5 666 0 858 28.8 999 701 5.3 +la02 10x5 655 0 904 38.0 999 729 11.3 +la03 10x5 597 0 775 29.8 999 673 12.7 +la04 10x5 590 0 854 44.7 999 667 13.1 +la05 10x5 593 0 629 6.1 999 593 0.0 +la06 15x5 926 0 1015 9.6 999 943 1.8 +la07 15x5 890 0 1096 23.1 999 981 10.2 +la08 15x5 863 0 1102 27.7 999 944 9.4 +la09 15x5 951 0 1024 7.7 999 964 1.4 +AVG - - 0.1 - 24.5 999.0 - 9.7 + + + Greedy-SPT Greedy-LRPT Greedy-EST_SPT Greedy-EST_LRPT +instance size best runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart +aaa1 2x3 11 6 16 45.5 0 11 0.0 0 11 0.0 1 11 0.0 +ft06 6x6 55 2 108 96.4 2 74 34.5 1 88 60.0 1 61 10.9 +ft10 10x10 930 3 2569 176.2 2 1289 38.6 2 1074 15.5 2 1108 19.1 +ft20 20x5 1165 3 2765 137.3 3 1955 67.8 2 1267 8.8 1 1501 28.8 +la01 10x5 666 1 1464 119.8 0 845 26.9 1 751 12.8 1 735 10.4 +la02 10x5 655 1 1943 196.6 0 982 49.9 0 821 25.3 1 817 24.7 +la03 10x5 597 1 1053 76.4 0 797 33.5 0 672 12.6 0 696 16.6 +la04 10x5 590 1 1491 152.7 0 1000 69.5 1 711 20.5 0 758 28.5 +la05 10x5 593 0 1521 156.5 0 666 12.3 0 610 2.9 0 593 0.0 +la06 15x5 926 1 2372 156.2 1 1151 24.3 0 1200 29.6 0 926 0.0 +la07 15x5 890 1 1823 104.8 1 1108 24.5 1 1034 16.2 1 970 9.0 +la08 15x5 863 0 2309 167.6 0 1196 38.6 1 942 9.2 1 943 9.3 +la09 15x5 951 0 2638 177.4 0 1233 29.7 0 1045 9.9 1 1015 6.7 +AVG - - 1.5 - 135.6 0.7 - 34.6 0.7 - 17.2 0.8 - 12.6 + + + Descent-SPT Descent-LRPT Descent-EST_SPT Descent-EST_LRPT +instance size best runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart +aaa1 2x3 11 14 11 0.0 1 11 0.0 1 11 0.0 1 11 0.0 +ft06 6x6 55 21 65 18.2 4 58 5.5 5 72 30.9 3 55 0.0 +ft10 10x10 930 66 1379 48.3 23 1073 15.4 5 1017 9.4 2 1108 19.1 +ft20 20x5 1165 79 1574 35.1 31 1513 29.9 1 1267 8.8 1 1501 28.8 +la01 10x5 666 11 907 36.2 4 666 0.0 2 686 3.0 4 695 4.4 +la02 10x5 655 8 869 32.7 2 806 23.1 3 685 4.6 1 782 19.4 +la03 10x5 597 2 877 46.9 1 725 21.4 0 666 11.6 1 696 16.6 +la04 10x5 590 6 852 44.4 2 794 34.6 1 702 19.0 0 747 26.6 +la05 10x5 593 12 742 25.1 2 621 4.7 0 610 2.9 0 593 0.0 +la06 15x5 926 11 1348 45.6 3 952 2.8 2 963 4.0 0 926 0.0 +la07 15x5 890 14 1087 22.1 3 965 8.4 0 1034 16.2 5 923 3.7 +la08 15x5 863 20 1320 53.0 2 1082 25.4 0 933 8.1 0 909 5.3 +la09 15x5 951 29 1433 50.7 3 1084 14.0 1 975 2.5 1 985 3.6 +AVG - - 22.5 - 35.2 6.2 - 14.2 1.6 - 9.3 1.5 - 9.8 + + + Taboo-EST_LRPT(1,1) +instance size best runtime makespan ecart +aaa1 2x3 11 17 11 0.0 +ft06 6x6 55 20 57 3.6 +ft10 10x10 930 13 1108 19.1 +ft20 20x5 1165 9 1501 28.8 +la01 10x5 666 4 695 4.4 +la02 10x5 655 3 803 22.6 +la03 10x5 597 2 696 16.6 +la04 10x5 590 2 747 26.6 +la05 10x5 593 1 593 0.0 +la06 15x5 926 2 926 0.0 +la07 15x5 890 3 951 6.9 +la08 15x5 863 12 909 5.3 +la09 15x5 951 2 985 3.6 +AVG - - 6.9 - 10.6 + + + Taboo-EST_LRPT(1,10) Taboo-EST_LRPT(2,10) Taboo-EST_LRPT(3,10) Taboo-EST_LRPT(4,10) Taboo-EST_LRPT(5,10) Taboo-EST_LRPT(6,10) Taboo-EST_LRPT(7,10) Taboo-EST_LRPT(8,10) Taboo-EST_LRPT(9,10) Taboo-EST_LRPT(10,10) +instance size best runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart +aaa1 2x3 11 25 11 0.0 4 11 0.0 4 11 0.0 2 11 0.0 2 11 0.0 2 11 0.0 2 11 0.0 1 11 0.0 2 11 0.0 3 11 0.0 +ft06 6x6 55 14 55 0.0 11 55 0.0 11 55 0.0 7 55 0.0 8 55 0.0 7 55 0.0 6 55 0.0 16 55 0.0 4 55 0.0 4 55 0.0 +ft10 10x10 930 21 1108 19.1 23 1108 19.1 19 1096 17.8 14 1108 19.1 19 1108 19.1 14 1108 19.1 12 1108 19.1 11 1108 19.1 11 1108 19.1 11 1108 19.1 +ft20 20x5 1165 7 1501 28.8 7 1465 25.8 8 1465 25.8 15 1465 25.8 5 1465 25.8 4 1465 25.8 5 1465 25.8 5 1465 25.8 5 1465 25.8 5 1465 25.8 +la01 10x5 666 4 695 4.4 2 692 3.9 3 692 3.9 2 692 3.9 3 692 3.9 2 692 3.9 2 692 3.9 2 692 3.9 2 692 3.9 2 692 3.9 +la02 10x5 655 3 769 17.4 3 769 17.4 3 769 17.4 2 753 15.0 2 753 15.0 3 753 15.0 3 753 15.0 2 753 15.0 2 753 15.0 3 753 15.0 +la03 10x5 597 2 692 15.9 2 692 15.9 6 689 15.4 2 689 15.4 2 692 15.9 4 692 15.9 3 692 15.9 2 692 15.9 3 692 15.9 2 692 15.9 +la04 10x5 590 3 696 18.0 2 696 18.0 3 696 18.0 2 696 18.0 2 702 19.0 2 703 19.2 2 703 19.2 1 694 17.6 2 694 17.6 2 694 17.6 +la05 10x5 593 3 593 0.0 2 593 0.0 1 593 0.0 1 593 0.0 1 593 0.0 1 593 0.0 1 593 0.0 1 593 0.0 1 593 0.0 1 593 0.0 +la06 15x5 926 2 926 0.0 2 926 0.0 2 926 0.0 2 926 0.0 2 926 0.0 2 926 0.0 1 926 0.0 3 926 0.0 2 926 0.0 1 926 0.0 +la07 15x5 890 7 912 2.5 5 912 2.5 4 912 2.5 8 912 2.5 4 912 2.5 4 912 2.5 5 912 2.5 4 912 2.5 4 912 2.5 4 912 2.5 +la08 15x5 863 4 902 4.5 4 902 4.5 5 902 4.5 4 902 4.5 8 902 4.5 6 902 4.5 5 902 4.5 4 902 4.5 3 902 4.5 3 902 4.5 +la09 15x5 951 3 951 0.0 2 951 0.0 3 951 0.0 15 985 3.6 6 985 3.6 5 951 0.0 4 951 0.0 4 951 0.0 4 951 0.0 6 951 0.0 +AVG - - 7.5 - 8.5 5.3 - 8.2 5.5 - 8.1 5.8 - 8.3 4.9 - 8.4 4.3 - 8.1 3.9 - 8.1 4.3 - 8.0 3.5 - 8.0 3.6 - 8.0 + + + Taboo-EST_LRPT(1,100) Taboo-EST_LRPT(6,100) Taboo-EST_LRPT(8,100) Taboo-EST_LRPT(10,100) Taboo-EST_LRPT(12,100) Taboo-EST_LRPT(14,100) Taboo-EST_LRPT(20,100) Taboo-EST_LRPT(50,100) Taboo-EST_LRPT(100,100) +instance size best runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart +aaa1 2x3 11 48 11 0.0 13 11 0.0 9 11 0.0 6 11 0.0 5 11 0.0 5 11 0.0 3 11 0.0 2 11 0.0 3 11 0.0 +ft06 6x6 55 43 55 0.0 36 55 0.0 27 55 0.0 20 55 0.0 20 55 0.0 15 55 0.0 17 55 0.0 10 55 0.0 6 55 0.0 +ft10 10x10 930 95 1108 19.1 63 1037 11.5 59 1041 11.9 52 1032 11.0 60 1048 12.7 82 1046 12.5 55 1054 13.3 48 1098 18.1 34 1098 18.1 +ft20 20x5 1165 42 1501 28.8 40 1329 14.1 41 1329 14.1 46 1311 12.5 37 1325 13.7 43 1329 14.1 51 1352 16.1 23 1399 20.1 18 1400 20.2 +la01 10x5 666 23 695 4.4 9 666 0.0 7 666 0.0 19 666 0.0 16 690 3.6 13 688 3.3 15 671 0.8 8 690 3.6 6 692 3.9 +la02 10x5 655 34 737 12.5 17 722 10.2 22 694 6.0 15 706 7.8 16 732 11.8 13 736 12.4 11 736 12.4 9 736 12.4 11 745 13.7 +la03 10x5 597 21 692 15.9 21 651 9.0 21 676 13.2 17 637 6.7 15 678 13.6 16 670 12.2 14 617 3.4 10 648 8.5 7 692 15.9 +la04 10x5 590 21 696 18.0 22 638 8.1 26 646 9.5 23 648 9.8 14 639 8.3 14 651 10.3 12 647 9.7 9 674 14.2 5 674 14.2 +la05 10x5 593 11 593 0.0 8 593 0.0 8 593 0.0 5 593 0.0 5 593 0.0 5 593 0.0 4 593 0.0 4 593 0.0 5 593 0.0 +la06 15x5 926 31 926 0.0 11 926 0.0 8 926 0.0 8 926 0.0 8 926 0.0 8 926 0.0 11 926 0.0 7 926 0.0 7 926 0.0 +la07 15x5 890 33 890 0.0 14 890 0.0 13 890 0.0 17 890 0.0 11 890 0.0 15 890 0.0 11 890 0.0 10 890 0.0 11 890 0.0 +la08 15x5 863 30 902 4.5 22 863 0.0 25 866 0.3 23 863 0.0 24 863 0.0 21 863 0.0 14 863 0.0 13 878 1.7 8 902 4.5 +la09 15x5 951 15 951 0.0 11 951 0.0 11 951 0.0 9 951 0.0 8 951 0.0 9 951 0.0 23 951 0.0 13 951 0.0 13 951 0.0 +AVG - - 34.4 - 7.9 22.1 - 4.1 21.3 - 4.2 20.0 - 3.7 18.4 - 4.9 19.9 - 5.0 18.5 - 4.3 12.8 - 6.0 10.3 - 7.0 + + + Taboo-EST_LRPT(1,1000) Taboo-EST_LRPT(6,1000) Taboo-EST_LRPT(8,1000) Taboo-EST_LRPT(10,1000) Taboo-EST_LRPT(12,1000) Taboo-EST_LRPT(14,1000) Taboo-EST_LRPT(20,1000) Taboo-EST_LRPT(50,1000) Taboo-EST_LRPT(100,1000) +instance size best runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart +aaa1 2x3 11 99 11 0.0 24 11 0.0 23 11 0.0 25 11 0.0 24 11 0.0 21 11 0.0 18 11 0.0 14 11 0.0 12 11 0.0 +ft06 6x6 55 160 55 0.0 137 55 0.0 133 55 0.0 99 55 0.0 103 55 0.0 76 55 0.0 73 55 0.0 49 55 0.0 55 55 0.0 +ft10 10x10 930 583 1108 19.1 622 1013 8.9 569 983 5.7 541 978 5.2 573 978 5.2 530 1007 8.3 535 1029 10.6 360 1045 12.4 251 1015 9.1 +ft20 20x5 1165 383 1501 28.8 396 1217 4.5 438 1262 8.3 366 1243 6.7 294 1255 7.7 321 1218 4.5 302 1219 4.6 209 1246 7.0 168 1271 9.1 +la01 10x5 666 196 695 4.4 61 666 0.0 52 666 0.0 64 666 0.0 65 666 0.0 76 666 0.0 68 666 0.0 52 666 0.0 54 666 0.0 +la02 10x5 655 244 737 12.5 170 667 1.8 177 665 1.5 158 665 1.5 142 662 1.1 134 655 0.0 108 671 2.4 69 692 5.6 58 720 9.9 +la03 10x5 597 165 692 15.9 133 628 5.2 152 620 3.9 143 603 1.0 140 603 1.0 130 603 1.0 108 603 1.0 76 615 3.0 56 615 3.0 +la04 10x5 590 203 696 18.0 129 638 8.1 160 598 1.4 151 599 1.5 148 604 2.4 75 637 8.0 104 597 1.2 91 620 5.1 72 620 5.1 +la05 10x5 593 93 593 0.0 56 593 0.0 59 593 0.0 49 593 0.0 49 593 0.0 45 593 0.0 45 593 0.0 45 593 0.0 40 593 0.0 +la06 15x5 926 169 926 0.0 90 926 0.0 80 926 0.0 82 926 0.0 77 926 0.0 72 926 0.0 76 926 0.0 64 926 0.0 65 926 0.0 +la07 15x5 890 164 890 0.0 92 890 0.0 94 890 0.0 84 890 0.0 82 890 0.0 79 890 0.0 81 890 0.0 69 890 0.0 72 890 0.0 +la08 15x5 863 326 902 4.5 103 863 0.0 105 863 0.0 90 863 0.0 92 863 0.0 82 863 0.0 82 863 0.0 72 863 0.0 72 863 0.0 +la09 15x5 951 151 951 0.0 85 951 0.0 81 951 0.0 80 951 0.0 79 951 0.0 78 951 0.0 78 951 0.0 78 951 0.0 62 951 0.0 +AVG - - 225.8 - 7.9 161.4 - 2.2 163.3 - 1.6 148.6 - 1.2 143.7 - 1.3 132.2 - 1.7 129.1 - 1.5 96.0 - 2.5 79.8 - 2.8 + + + Taboo-EST_LRPT(1,5000) Taboo-EST_LRPT(6,5000) Taboo-EST_LRPT(8,5000) Taboo-EST_LRPT(10,5000) Taboo-EST_LRPT(12,5000) Taboo-EST_LRPT(14,5000) Taboo-EST_LRPT(20,5000) Taboo-EST_LRPT(50,5000) Taboo-EST_LRPT(100,5000) +instance size best runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart runtime makespan ecart +aaa1 2x3 11 265 11 0.0 101 11 0.0 40 11 0.0 34 11 0.0 35 11 0.0 27 11 0.0 38 11 0.0 25 11 0.0 30 11 0.0 +ft06 6x6 55 675 55 0.0 665 55 0.0 405 55 0.0 504 55 0.0 489 55 0.0 329 55 0.0 286 55 0.0 267 55 0.0 222 55 0.0 +ft10 10x10 930 1000 1108 19.1 1000 1002 7.7 1000 954 2.6 1000 978 5.2 1000 976 4.9 1000 995 7.0 1000 1007 8.3 1000 1036 11.4 1000 1015 9.1 +ft20 20x5 1165 1000 1501 28.8 1000 1217 4.5 1000 1262 8.3 1000 1193 2.4 1000 1255 7.7 1000 1192 2.3 1000 1187 1.9 1000 1189 2.1 699 1240 6.4 +la01 10x5 666 999 695 4.4 320 666 0.0 278 666 0.0 273 666 0.0 274 666 0.0 280 666 0.0 255 666 0.0 240 666 0.0 242 666 0.0 +la02 10x5 655 1000 737 12.5 959 667 1.8 722 660 0.8 612 655 0.0 604 658 0.5 620 655 0.0 473 655 0.0 420 660 0.8 329 669 2.1 +la03 10x5 597 891 692 15.9 510 628 5.2 690 605 1.3 380 603 1.0 690 603 1.0 329 603 1.0 440 603 1.0 414 615 3.0 278 615 3.0 +la04 10x5 590 1000 696 18.0 710 638 8.1 677 598 1.4 382 599 1.5 477 602 2.0 339 637 8.0 381 597 1.2 446 598 1.4 332 616 4.4 +la05 10x5 593 504 593 0.0 302 593 0.0 278 593 0.0 271 593 0.0 250 593 0.0 249 593 0.0 252 593 0.0 214 593 0.0 223 593 0.0 +la06 15x5 926 794 926 0.0 471 926 0.0 412 926 0.0 417 926 0.0 406 926 0.0 391 926 0.0 367 926 0.0 360 926 0.0 338 926 0.0 +la07 15x5 890 787 890 0.0 462 890 0.0 477 890 0.0 474 890 0.0 474 890 0.0 421 890 0.0 395 890 0.0 373 890 0.0 381 890 0.0 +la08 15x5 863 1000 902 4.5 473 863 0.0 455 863 0.0 445 863 0.0 421 863 0.0 411 863 0.0 387 863 0.0 372 863 0.0 351 863 0.0 +la09 15x5 951 786 951 0.0 482 951 0.0 453 951 0.0 426 951 0.0 406 951 0.0 392 951 0.0 388 951 0.0 367 951 0.0 357 951 0.0 +AVG - - 823.2 - 7.9 573.5 - 2.1 529.8 - 1.1 478.3 - 0.8 502.0 - 1.2 445.2 - 1.4 435.5 - 1.0 422.9 - 1.4 367.8 - 1.9 + + diff --git a/instances/aaa2 b/instances/aaa2 index e9bd83f..db3820b 100644 --- a/instances/aaa2 +++ b/instances/aaa2 @@ -1,5 +1,5 @@ # Example instance 3 3 # num-jobs num-tasks -2 3 0 3 1 2 +2 4 0 3 1 2 1 7 0 6 2 5 0 2 1 10 2 4 \ No newline at end of file diff --git a/instances/aaa3 b/instances/aaa3 new file mode 100644 index 0000000..4f918a3 --- /dev/null +++ b/instances/aaa3 @@ -0,0 +1,5 @@ +# Example instance +3 3 # num-jobs num-tasks +1 4 2 3 0 2 +1 7 0 6 2 5 +0 2 1 10 2 4 \ No newline at end of file diff --git a/src/main/java/jobshop/BestKnownResult.java b/src/main/java/jobshop/BestKnownResult.java index e0cf7ea..55d5f9b 100644 --- a/src/main/java/jobshop/BestKnownResult.java +++ b/src/main/java/jobshop/BestKnownResult.java @@ -1,180 +1,189 @@ -package jobshop; - -import java.util.Arrays; -import java.util.HashMap; - -public class BestKnownResult { - - public static boolean isKnown(String instanceName) { - return bests.containsKey(instanceName); - } - - public static int of(String instanceName) { - if(!bests.containsKey(instanceName)) { - throw new RuntimeException("Unknown best result for "+instanceName); - } - return bests.get(instanceName); - } - - static private HashMap bests; - static String[] instances; - static { - bests = new HashMap<>(); - bests.put("aaa1", 11); - bests.put("abz5", 1234); - bests.put("abz6", 943); - bests.put("abz7", 656); - bests.put("abz8", 665); - bests.put("abz9", 679); - bests.put("ft06", 55); - bests.put("ft10", 930); - bests.put("ft20", 1165); - bests.put("la01", 666); - bests.put("la02", 655); - bests.put("la03", 597); - bests.put("la04", 590); - bests.put("la05", 593); - bests.put("la06", 926); - bests.put("la07", 890); - bests.put("la08", 863); - bests.put("la09", 951); - bests.put("la10", 958); - bests.put("la11", 1222); - bests.put("la12", 1039); - bests.put("la13", 1150); - bests.put("la14", 1292); - bests.put("la15", 1207); - bests.put("la16", 945); - bests.put("la17", 784); - bests.put("la18", 848); - bests.put("la19", 842); - bests.put("la20", 902); - bests.put("la21", 1046); - bests.put("la22", 927); - bests.put("la23", 1032); - bests.put("la24", 935); - bests.put("la25", 977); - bests.put("la26", 1218); - bests.put("la27", 1235); - bests.put("la28", 1216); - bests.put("la29", 1152); - bests.put("la30", 1355); - bests.put("la31", 1784); - bests.put("la32", 1850); - bests.put("la33", 1719); - bests.put("la34", 1721); - bests.put("la35", 1888); - bests.put("la36", 1268); - bests.put("la37", 1397); - bests.put("la38", 1196); - bests.put("la39", 1233); - bests.put("la40", 1222); - bests.put("orb01", 1059); - bests.put("orb02", 888); - bests.put("orb03", 1005); - bests.put("orb04", 1005); - bests.put("orb05", 887); - bests.put("orb06", 1010); - bests.put("orb07", 397); - bests.put("orb08", 899); - bests.put("orb09", 934); - bests.put("orb10", 944); - bests.put("swv01", 1407); - bests.put("swv02", 1475); - bests.put("swv03", 1398); - bests.put("swv04", 1474); - bests.put("swv05", 1424); - bests.put("swv06", 1678); - bests.put("swv07", 1600); - bests.put("swv08", 1763); - bests.put("swv09", 1661); - bests.put("swv10", 1767); - bests.put("swv11", 2991); - bests.put("swv12", 3003); - bests.put("swv13", 3104); - bests.put("swv14", 2968); - bests.put("swv15", 2904); - bests.put("swv16", 2924); - bests.put("swv17", 2794); - bests.put("swv18", 2852); - bests.put("swv19", 2843); - bests.put("swv20", 2823); - bests.put("yn1", 885); - bests.put("yn2", 909); - bests.put("yn3", 892); - bests.put("yn4", 968); - bests.put("ta01", 1231); - bests.put("ta02", 1244); - bests.put("ta03", 1218); - bests.put("ta04", 1175); - bests.put("ta05", 1224); - bests.put("ta06", 1238); - bests.put("ta07", 1227); - bests.put("ta08", 1217); - bests.put("ta09", 1274); - bests.put("ta10", 1241); - bests.put("ta11", 1361); - bests.put("ta12", 1367); - bests.put("ta13", 1342); - bests.put("ta14", 1345); - bests.put("ta15", 1340); - bests.put("ta16", 1360); - bests.put("ta17", 1462); - bests.put("ta18", 1396); - bests.put("ta19", 1335); - bests.put("ta20", 1351); - bests.put("ta21", 1644); - bests.put("ta22", 1600); - bests.put("ta23", 1557); - bests.put("ta24", 1647); - bests.put("ta25", 1595); - bests.put("ta26", 1645); - bests.put("ta27", 1680); - bests.put("ta28", 1614); - bests.put("ta29", 1635); - bests.put("ta30", 1584); - bests.put("ta31", 1764); - bests.put("ta32", 1796); - bests.put("ta33", 1793); - bests.put("ta34", 1829); - bests.put("ta35", 2007); - bests.put("ta36", 1819); - bests.put("ta37", 1778); - bests.put("ta38", 1673); - bests.put("ta39", 1795); - bests.put("ta40", 1674); - bests.put("ta41", 2018); - bests.put("ta42", 1956); - bests.put("ta43", 1859); - bests.put("ta44", 1984); - bests.put("ta45", 2000); - bests.put("ta46", 2021); - bests.put("ta47", 1903); - bests.put("ta48", 1952); - bests.put("ta49", 1968); - bests.put("ta50", 1926); - bests.put("ta51", 2760); - bests.put("ta52", 2756); - bests.put("ta53", 2717); - bests.put("ta54", 2839); - bests.put("ta55", 2679); - bests.put("ta56", 2781); - bests.put("ta57", 2943); - bests.put("ta58", 2885); - bests.put("ta59", 2655); - bests.put("ta60", 2723); - bests.put("ta61", 2868); - bests.put("ta62", 2869); - bests.put("ta63", 2755); - bests.put("ta64", 2702); - bests.put("ta65", 2725); - bests.put("ta66", 2845); - bests.put("ta67", 2825); - bests.put("ta68", 2784); - bests.put("ta69", 3071); - bests.put("ta70", 2995); - instances = bests.keySet().toArray(new String[0]); - Arrays.sort(instances); - } - -} +package jobshop; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class BestKnownResult { + + public static boolean isKnown(String instanceName) { + return bests.containsKey(instanceName); + } + + public static List instancesMatching(String namePrefix) { + return Arrays.stream(instances) + .filter(i -> i.startsWith(namePrefix)) + .sorted() + .collect(Collectors.toList()); + } + + public static int of(String instanceName) { + if(!bests.containsKey(instanceName)) { + throw new RuntimeException("Unknown best result for "+instanceName); + } + return bests.get(instanceName); + } + + static private HashMap bests; + static String[] instances; + static { + bests = new HashMap<>(); + bests.put("aaa1", 11); + bests.put("abz5", 1234); + bests.put("abz6", 943); + bests.put("abz7", 656); + bests.put("abz8", 665); + bests.put("abz9", 679); + bests.put("ft06", 55); + bests.put("ft10", 930); + bests.put("ft20", 1165); + bests.put("la01", 666); + bests.put("la02", 655); + bests.put("la03", 597); + bests.put("la04", 590); + bests.put("la05", 593); + bests.put("la06", 926); + bests.put("la07", 890); + bests.put("la08", 863); + bests.put("la09", 951); + bests.put("la10", 958); + bests.put("la11", 1222); + bests.put("la12", 1039); + bests.put("la13", 1150); + bests.put("la14", 1292); + bests.put("la15", 1207); + bests.put("la16", 945); + bests.put("la17", 784); + bests.put("la18", 848); + bests.put("la19", 842); + bests.put("la20", 902); + bests.put("la21", 1046); + bests.put("la22", 927); + bests.put("la23", 1032); + bests.put("la24", 935); + bests.put("la25", 977); + bests.put("la26", 1218); + bests.put("la27", 1235); + bests.put("la28", 1216); + bests.put("la29", 1152); + bests.put("la30", 1355); + bests.put("la31", 1784); + bests.put("la32", 1850); + bests.put("la33", 1719); + bests.put("la34", 1721); + bests.put("la35", 1888); + bests.put("la36", 1268); + bests.put("la37", 1397); + bests.put("la38", 1196); + bests.put("la39", 1233); + bests.put("la40", 1222); + bests.put("orb01", 1059); + bests.put("orb02", 888); + bests.put("orb03", 1005); + bests.put("orb04", 1005); + bests.put("orb05", 887); + bests.put("orb06", 1010); + bests.put("orb07", 397); + bests.put("orb08", 899); + bests.put("orb09", 934); + bests.put("orb10", 944); + bests.put("swv01", 1407); + bests.put("swv02", 1475); + bests.put("swv03", 1398); + bests.put("swv04", 1474); + bests.put("swv05", 1424); + bests.put("swv06", 1678); + bests.put("swv07", 1600); + bests.put("swv08", 1763); + bests.put("swv09", 1661); + bests.put("swv10", 1767); + bests.put("swv11", 2991); + bests.put("swv12", 3003); + bests.put("swv13", 3104); + bests.put("swv14", 2968); + bests.put("swv15", 2904); + bests.put("swv16", 2924); + bests.put("swv17", 2794); + bests.put("swv18", 2852); + bests.put("swv19", 2843); + bests.put("swv20", 2823); + bests.put("yn1", 885); + bests.put("yn2", 909); + bests.put("yn3", 892); + bests.put("yn4", 968); + bests.put("ta01", 1231); + bests.put("ta02", 1244); + bests.put("ta03", 1218); + bests.put("ta04", 1175); + bests.put("ta05", 1224); + bests.put("ta06", 1238); + bests.put("ta07", 1227); + bests.put("ta08", 1217); + bests.put("ta09", 1274); + bests.put("ta10", 1241); + bests.put("ta11", 1361); + bests.put("ta12", 1367); + bests.put("ta13", 1342); + bests.put("ta14", 1345); + bests.put("ta15", 1340); + bests.put("ta16", 1360); + bests.put("ta17", 1462); + bests.put("ta18", 1396); + bests.put("ta19", 1335); + bests.put("ta20", 1351); + bests.put("ta21", 1644); + bests.put("ta22", 1600); + bests.put("ta23", 1557); + bests.put("ta24", 1647); + bests.put("ta25", 1595); + bests.put("ta26", 1645); + bests.put("ta27", 1680); + bests.put("ta28", 1614); + bests.put("ta29", 1635); + bests.put("ta30", 1584); + bests.put("ta31", 1764); + bests.put("ta32", 1796); + bests.put("ta33", 1793); + bests.put("ta34", 1829); + bests.put("ta35", 2007); + bests.put("ta36", 1819); + bests.put("ta37", 1778); + bests.put("ta38", 1673); + bests.put("ta39", 1795); + bests.put("ta40", 1674); + bests.put("ta41", 2018); + bests.put("ta42", 1956); + bests.put("ta43", 1859); + bests.put("ta44", 1984); + bests.put("ta45", 2000); + bests.put("ta46", 2021); + bests.put("ta47", 1903); + bests.put("ta48", 1952); + bests.put("ta49", 1968); + bests.put("ta50", 1926); + bests.put("ta51", 2760); + bests.put("ta52", 2756); + bests.put("ta53", 2717); + bests.put("ta54", 2839); + bests.put("ta55", 2679); + bests.put("ta56", 2781); + bests.put("ta57", 2943); + bests.put("ta58", 2885); + bests.put("ta59", 2655); + bests.put("ta60", 2723); + bests.put("ta61", 2868); + bests.put("ta62", 2869); + bests.put("ta63", 2755); + bests.put("ta64", 2702); + bests.put("ta65", 2725); + bests.put("ta66", 2845); + bests.put("ta67", 2825); + bests.put("ta68", 2784); + bests.put("ta69", 3071); + bests.put("ta70", 2995); + instances = bests.keySet().toArray(new String[0]); + Arrays.sort(instances); + } + +} diff --git a/src/main/java/jobshop/DebuggingMain.java b/src/main/java/jobshop/DebuggingMain.java index 0969843..29a8c9b 100644 --- a/src/main/java/jobshop/DebuggingMain.java +++ b/src/main/java/jobshop/DebuggingMain.java @@ -1,153 +1,189 @@ -package jobshop; - -import jobshop.encodings.JobNumbers; -import jobshop.encodings.ResourceOrder; -import jobshop.encodings.Task; -import jobshop.solvers.DescentSolver; -import jobshop.solvers.GreedySolver; -import jobshop.solvers.DescentSolver.Block; -import jobshop.solvers.GreedySolver.PriorityESTRule; -import jobshop.solvers.GreedySolver.PriorityRule; - -import java.io.IOException; -import java.nio.file.Paths; -import java.util.List; - -public class DebuggingMain { - - public static void main(String[] args) { - try { - // load the aaa1 instance - Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); - - // construit une solution dans la représentation par - // numéro de jobs : [0 1 1 0 0 1] - // Note : cette solution a aussi été vue dans les exercices (section 3.3) - // mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2] - JobNumbers enc = new JobNumbers(instance); - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - - System.out.println("\nJOB NUMBER ENCODING: " + enc + "\n"); - - Schedule sched = enc.toSchedule(); - - System.out.println("SCHEDULE:\n" + sched); - System.out.println("VALID: " + sched.isValid() + "\n"); - System.out.println("MAKESPAN: " + sched.makespan() + "\n"); - - System.out.println("---------------------------------------------\n"); - - ResourceOrder ro = new ResourceOrder(instance); - ro.tasksByMachine[0][0] = new Task(0,0); - ro.tasksByMachine[0][1] = new Task(1,1); - ro.tasksByMachine[1][0] = new Task(1,0); - ro.tasksByMachine[1][1] = new Task(0,1); - ro.tasksByMachine[2][0] = new Task(0,2); - ro.tasksByMachine[2][1] = new Task(1,2); - - System.out.println("RESOURCE ORDER ENCODING:\n" + ro + "\n"); - - System.out.println("---------------------------------------------\n"); - - // load the aaa2 instance - Instance instance2 = Instance.fromFile(Paths.get("instances/aaa2")); - - ResourceOrder ro2 = new ResourceOrder(instance2); - ro2.tasksByMachine[0][0] = new Task(2,0); //O7: Job 2, Task 0 (Machine 0) - ro2.tasksByMachine[0][1] = new Task(1,1); //O5: Job 1, Task 1 (Machine 0) - ro2.tasksByMachine[0][2] = new Task(0,1); //O2: Job 0, Task 1 (Machine 0) - ro2.tasksByMachine[1][0] = new Task(1,0); //O4: Job 1, Task 0 (Machine 1) - ro2.tasksByMachine[1][1] = new Task(2,1); //O8: Job 2, Task 1 (Machine 1) - ro2.tasksByMachine[1][2] = new Task(0,2); //O3: Job 0, Task 2 (Machine 1) - ro2.tasksByMachine[2][0] = new Task(2,2); //O9: Job 2, Task 2 (Machine 2) - ro2.tasksByMachine[2][1] = new Task(0,0); //O1: Job 0, Task 0 (Machine 2) - ro2.tasksByMachine[2][2] = new Task(1,2); //O6: Job 1, Task 2 (Machine 2) - - System.out.println("RESOURCE ORDER ENCODING:\n" + ro2 + "\n"); - - System.out.println("---------------------------------------------\n"); - - System.out.println("Default Solver\n"); - sched = ro.toSchedule(); - - System.out.println("SCHEDULE:\n" + sched); - System.out.println("VALID: " + sched.isValid()); - System.out.println("MAKESPAN: " + sched.makespan()); - - System.out.println("---------------------------------------------\n"); - - JobNumbers jo = JobNumbers.fromSchedule(sched); - System.out.println("JOB NUMBER ENCODING (FROM_SCHEDULE): " + jo + "\n"); - - System.out.println("---------------------------------------------\n"); - System.out.println("Greedy Solver: STP"); - PriorityRule SPT = PriorityRule.SPT; - Solver solverSPT = new GreedySolver(SPT); - Result resultSPT = solverSPT.solve(instance, System.currentTimeMillis() + 10); - sched = resultSPT.schedule; - - System.out.println("SCHEDULE:\n" + sched); - System.out.println("VALID: " + sched.isValid()); - System.out.println("MAKESPAN: " + sched.makespan()); - - System.out.println("---------------------------------------------\n"); - System.out.println("Greedy Solver: LRPT\n"); - PriorityRule LRPT = PriorityRule.LRPT; - Solver solverLRPT = new GreedySolver(LRPT); - Result resultLRPT = solverLRPT.solve(instance, System.currentTimeMillis() + 10); - sched = resultLRPT.schedule; - - System.out.println("SCHEDULE:\n" + sched); - System.out.println("VALID: " + sched.isValid()); - System.out.println("MAKESPAN: " + sched.makespan()); - - System.out.println("---------------------------------------------\n"); - System.out.println("Greedy Solver: EST_SPT\n"); - PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT; - Solver solverEST_SPT = new GreedySolver(EST_SPT); - Result resultEST_SPT = solverEST_SPT.solve(instance, System.currentTimeMillis() + 10); - sched = resultEST_SPT.schedule; - - System.out.println("SCHEDULE:\n" + sched); - System.out.println("VALID: " + sched.isValid()); - System.out.println("MAKESPAN: " + sched.makespan()); - - System.out.println("---------------------------------------------\n"); - System.out.println("Greedy Solver: ic void applyOn(ResourceOrder order) {\r\n" + - " throw new UnsupportedOperationException();EST_SPT\n"); - PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT; - Solver solverEST_LRPT = new GreedySolver(EST_SPT); - Result resultEST_LRPT = solverEST_LRPT.solve(instance, System.currentTimeMillis() + 10); - sched = resultEST_LRPT.schedule; - - System.out.println("SCHEDULE:\n" + sched); - System.out.println("VALID: " + sched.isValid()); - System.out.println("MAKESPAN: " + sched.makespan()); - - System.out.println("---------------------------------------------\n"); - System.out.println("Descent Solver: \n"); - DescentSolver solverDescent = new DescentSolver(); - List criticalBlockList = solverDescent.blocksOfCriticalPath(ro2); - for(Block b : criticalBlockList) { - System.out.println(b); - //System.out.println(solverDescent.neighbors(b)); - } - /* - sched = ro2.toSchedule(); - - System.out.println("SCHEDULE:\n" + sched); - System.out.println("VALID: " + sched.isValid()); - System.out.println("MAKESPAN: " + sched.makespan());*/ - - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - - } -} +package jobshop; + +import jobshop.encodings.JobNumbers; +import jobshop.encodings.ResourceOrder; +import jobshop.encodings.Task; +import jobshop.solvers.DescentSolver; +import jobshop.solvers.DescentSolver.Block; +import jobshop.solvers.DescentSolver.Swap; +import jobshop.solvers.GreedySolver; +import jobshop.solvers.GreedySolver.PriorityESTRule; +import jobshop.solvers.GreedySolver.PriorityRule; +import jobshop.solvers.TabooSolver; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.List; + +public class DebuggingMain { + + public static void main(String[] args) { + try { + // load the aaa1 instance + Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); + + // construit une solution dans la représentation par + // numéro de jobs : [0 1 1 0 0 1] + // Note : cette solution a aussi été vue dans les exercices (section 3.3) + // mais on commençait à compter à 1 ce qui donnait [1 2 2 1 1 2] + JobNumbers enc = new JobNumbers(instance); + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + + System.out.println("\nENCODING: " + enc); + + Schedule sched = enc.toSchedule(); + // TODO: make it print something meaningful + // by implementing the toString() method + System.out.println("SCHEDULE: " + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + System.out.println("---------------------------------------------\n"); + + ResourceOrder ro = new ResourceOrder(instance); + ro.tasksByMachine[0][0] = new Task(0,0); + ro.tasksByMachine[0][1] = new Task(1,1); + ro.tasksByMachine[1][0] = new Task(1,0); + ro.tasksByMachine[1][1] = new Task(0,1); + ro.tasksByMachine[2][0] = new Task(0,2); + ro.tasksByMachine[2][1] = new Task(1,2); + + System.out.println("RESOURCE ORDER ENCODING:\n" + ro + "\n"); + + System.out.println("---------------------------------------------\n"); + + // load the aaa2 instance + Instance instance2 = Instance.fromFile(Paths.get("instances/aaa2")); + + ResourceOrder ro2 = new ResourceOrder(instance2); + ro2.tasksByMachine[0][0] = new Task(2,0); //O7: Job 2, Task 0 (Machine 0) + ro2.tasksByMachine[0][1] = new Task(1,1); //O5: Job 1, Task 1 (Machine 0) + ro2.tasksByMachine[0][2] = new Task(0,1); //O2: Job 0, Task 1 (Machine 0) + ro2.tasksByMachine[1][0] = new Task(1,0); //O4: Job 1, Task 0 (Machine 1) + ro2.tasksByMachine[1][1] = new Task(2,1); //O8: Job 2, Task 1 (Machine 1) + ro2.tasksByMachine[1][2] = new Task(0,2); //O3: Job 0, Task 2 (Machine 1) + ro2.tasksByMachine[2][0] = new Task(2,2); //O9: Job 2, Task 2 (Machine 2) + ro2.tasksByMachine[2][1] = new Task(0,0); //O1: Job 0, Task 0 (Machine 2) + ro2.tasksByMachine[2][2] = new Task(1,2); //O6: Job 1, Task 2 (Machine 2) + + System.out.println("RESOURCE ORDER ENCODING:\n" + ro2 + "\n"); + + System.out.println("---------------------------------------------\n"); + + System.out.println("Default Solver\n"); + sched = ro.toSchedule(); + + System.out.println("SCHEDULE:\n" + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + System.out.println("---------------------------------------------\n"); + + /*JobNumbers jo = JobNumbers.fromSchedule(sched); + System.out.println("JOB NUMBER ENCODING (FROM_SCHEDULE): " + jo + "\n");*/ + + System.out.println("---------------------------------------------\n"); + System.out.println("Greedy Solver: STP"); + PriorityRule SPT = PriorityRule.SPT; + Solver solverSPT = new GreedySolver(SPT); + Result resultSPT = solverSPT.solve(instance, System.currentTimeMillis() + 10); + sched = resultSPT.schedule; + + System.out.println("SCHEDULE:\n" + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + System.out.println("---------------------------------------------\n"); + System.out.println("Greedy Solver: LRPT\n"); + PriorityRule LRPT = PriorityRule.LRPT; + Solver solverLRPT = new GreedySolver(LRPT); + Result resultLRPT = solverLRPT.solve(instance, System.currentTimeMillis() + 10); + sched = resultLRPT.schedule; + + System.out.println("SCHEDULE:\n" + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + System.out.println("---------------------------------------------\n"); + System.out.println("Greedy Solver: EST_SPT\n"); + PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT; + Solver solverEST_SPT = new GreedySolver(EST_SPT); + Result resultEST_SPT = solverEST_SPT.solve(instance, System.currentTimeMillis() + 10); + sched = resultEST_SPT.schedule; + + System.out.println("SCHEDULE:\n" + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + System.out.println("---------------------------------------------\n"); + System.out.println("Greedy Solver: EST_LRPT\n"); + PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT; + Solver solverEST_LRPT = new GreedySolver(EST_LRPT); + Result resultEST_LRPT = solverEST_LRPT.solve(instance, System.currentTimeMillis() + 10); + sched = resultEST_LRPT.schedule; + + System.out.println("SCHEDULE:\n" + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + System.out.println("---------------------------------------------\n"); + System.out.println("Descent Solver: [Executed with EST_LRPT]\n"); + + DescentSolver solverDescent = new DescentSolver(EST_LRPT); + + + System.out.print("****** TEST: blocksOfCriticalPath() ******\n"); + System.out.print("Number of Jobs : " + instance2.numJobs + "\n"); + System.out.print("Number of Tasks : " + instance2.numTasks + "\n"); + System.out.print("Number of Machines : " + instance2.numMachines + "\n\n"); + + + + List criticalBlockList = solverDescent.blocksOfCriticalPath(ro2); + for(Block b : criticalBlockList) { + System.out.println(b); + for(Swap s : solverDescent.neighbors(b)) { + System.out.println(s); + } + } + System.out.print("******************************************\n"); + + + Result resultDescent = solverDescent.solve(instance2, System.currentTimeMillis() + 10); + sched = resultDescent.schedule; + + System.out.println("SCHEDULE:\n" + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + System.out.println("---------------------------------------------\n"); + System.out.println("Taboo Solver: [Executed with EST_LRPT]\n"); + + TabooSolver solverTaboo = new TabooSolver(EST_LRPT, 50, 1000); + Result resultTaboo = solverTaboo.solve(instance2, System.currentTimeMillis() + 10); + sched = resultTaboo.schedule; + /* + ListcriticalBlockList2 = solverTaboo.blocksOfCriticalPath(ro2); + for(Block b : criticalBlockList2) { + System.out.println(b); + for(Swap s : solverTaboo.neighbors(b)) { + System.out.println(s); + } + } + */ + System.out.println("SCHEDULE:\n" + sched); + System.out.println("VALID: " + sched.isValid()); + System.out.println("MAKESPAN: " + sched.makespan()); + + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + + } +} diff --git a/src/main/java/jobshop/Encoding.java b/src/main/java/jobshop/Encoding.java index 026ad89..d6f0245 100644 --- a/src/main/java/jobshop/Encoding.java +++ b/src/main/java/jobshop/Encoding.java @@ -1,12 +1,12 @@ -package jobshop; - -public abstract class Encoding { - - public final Instance instance; - - public Encoding(Instance instance) { - this.instance = instance; - } - - public abstract Schedule toSchedule(); -} +package jobshop; + +public abstract class Encoding { + + public final Instance instance; + + public Encoding(Instance instance) { + this.instance = instance; + } + + public abstract Schedule toSchedule(); +} diff --git a/src/main/java/jobshop/Instance.java b/src/main/java/jobshop/Instance.java index 001faad..73bcce7 100644 --- a/src/main/java/jobshop/Instance.java +++ b/src/main/java/jobshop/Instance.java @@ -1,73 +1,79 @@ -package jobshop; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Iterator; -import java.util.Scanner; -import java.util.stream.Collectors; - -public class Instance { - - /** Number of jobs in the instance */ - public final int numJobs; - - /** Number of tasks per job */ - public final int numTasks; - - /** Number of machines, assumed to be same as number of tasks. */ - public final int numMachines; - - final int[][] durations; - final int[][] machines; - - public int duration(int job, int task) { - return durations[job][task]; - } - public int machine(int job, int task) { - return machines[job][task]; - } - - /** among the tasks of the given job, returns the task index that uses the given machine. */ - public int task_with_machine(int job, int wanted_machine) { - for(int task = 0 ; task < numTasks ; task++) { - if(machine(job, task) == wanted_machine) - return task; - } - throw new RuntimeException("No task targeting machine "+wanted_machine+" on job "+job); - } - - Instance(int numJobs, int numTasks) { - this.numJobs = numJobs; - this.numTasks = numTasks; - this.numMachines = numTasks; - - durations = new int[numJobs][numTasks]; - machines = new int[numJobs][numTasks]; - } - - public static Instance fromFile(Path path) throws IOException { - Iterator lines = Files.readAllLines(path).stream() - .filter(l -> !l.startsWith("#")) - .collect(Collectors.toList()) - .iterator(); - - Scanner header = new Scanner(lines.next()); - int num_jobs = header.nextInt(); - int num_tasks = header.nextInt(); - Instance pb = new Instance(num_jobs, num_tasks); - - for(int job = 0 ; job lines = Files.readAllLines(path).stream() + .filter(l -> !l.startsWith("#")) + .collect(Collectors.toList()) + .iterator(); + + Scanner header = new Scanner(lines.next()); + int num_jobs = header.nextInt(); + int num_tasks = header.nextInt(); + Instance pb = new Instance(num_jobs, num_tasks); + + for(int job = 0 ; job solvers; - static { - solvers = new HashMap<>(); - solvers.put("basic", new BasicSolver()); - solvers.put("random", new RandomSolver()); - // add new solvers here - PriorityRule SPT = PriorityRule.SPT; - solvers.put("greedySPT", new GreedySolver(SPT)); - PriorityRule LRPT = PriorityRule.LRPT; - solvers.put("greedyLRPT", new GreedySolver(LRPT)); - PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT; - solvers.put("greedyEST_SPT", new GreedySolver(EST_SPT)); - PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT; - solvers.put("greedyEST_LRPT", new GreedySolver(EST_LRPT)); - } - - @SuppressWarnings("unused") - public static void main(String[] args) { - ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build() - .defaultHelp(true) - .description("Solves jobshop problems."); - - parser.addArgument("-t", "--timeout") - .setDefault(1L) - .type(Long.class) - .help("Solver timeout in seconds for each instance"); - parser.addArgument("--solver") - .nargs("+") - .required(true) - .help("Solver(s) to use (space separated if more than one)"); - - parser.addArgument("--instance") - .nargs("+") - .required(true) - .help("Instance(s) to solve (space separated if more than one)"); - - Namespace ns = null; - try { - ns = parser.parseArgs(args); - } catch (ArgumentParserException e) { - parser.handleError(e); - System.exit(1); - } - - PrintStream output = System.out; - - long solveTimeMs = ns.getLong("timeout") * 1000; - - List solversToTest = ns.getList("solver"); - for(String solverName : solversToTest) { - if(!solvers.containsKey(solverName)) { - System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible."); - System.err.println(" Available solvers: " + solvers.keySet().toString()); - System.err.println(" You can provide your own solvers by adding them to the `Main.solvers` HashMap."); - System.exit(1); - } - } - List instances = ns.getList("instance"); - for(String instanceName : instances) { - if(!BestKnownResult.isKnown(instanceName)) { - System.err.println("ERROR: instance \"" + instanceName + "\" is not avalaible."); - System.err.println(" available instances: " + Arrays.toString(BestKnownResult.instances)); - System.exit(1); - } - } - - float[] runtimes = new float[solversToTest.size()]; - float[] distances = new float[solversToTest.size()]; - - try { - output.print( " ");; - for(String s : solversToTest) - output.printf("%-30s", s); - output.println(); - output.print("instance size best "); - for(String s : solversToTest) { - output.print("runtime makespan ecart "); - } - output.println(); - - - for(String instanceName : instances) { - int bestKnown = BestKnownResult.of(instanceName); - - - Path path = Paths.get("instances/", instanceName); - Instance instance = Instance.fromFile(path); - - output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown); - - for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) { - String solverName = solversToTest.get(solverId); - Solver solver = solvers.get(solverName); - long start = System.currentTimeMillis(); - long deadline = System.currentTimeMillis() + solveTimeMs; - Result result = solver.solve(instance, deadline); - long runtime = System.currentTimeMillis() - start; - - if(!result.schedule.isValid()) { - System.err.println("ERROR: solver returned an invalid schedule"); - System.exit(1); - } - - assert result.schedule.isValid(); - int makespan = result.schedule.makespan(); - float dist = 100f * (makespan - bestKnown) / (float) bestKnown; - runtimes[solverId] += (float) runtime / (float) instances.size(); - distances[solverId] += dist / (float) instances.size(); - - output.printf("%7d %8s %5.1f ", runtime, makespan, dist); - output.flush(); - } - output.println(); - - } - - - output.printf("%-8s %-5s %4s ", "AVG", "-", "-"); - for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) { - output.printf("%7.1f %8s %5.1f ", runtimes[solverId], "-", distances[solverId]); - } - - - - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } -} +package jobshop; + +import java.io.PrintStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + + +import jobshop.solvers.*; +import jobshop.solvers.GreedySolver.PriorityESTRule; +import jobshop.solvers.GreedySolver.PriorityRule; +import net.sourceforge.argparse4j.ArgumentParsers; +import net.sourceforge.argparse4j.inf.ArgumentParser; +import net.sourceforge.argparse4j.inf.ArgumentParserException; +import net.sourceforge.argparse4j.inf.Namespace; + + +public class Main { + // ******************************************** Main - Arguments ************************************************ // + // *** Basic + Random *** // + // --solver basic random --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + + // *** Greedy Solvers *** // + // --solver Greedy-SPT Greedy-LRPT Greedy-EST_SPT Greedy-EST_LRPT --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + + // *** Descent Solvers *** // + // --solver Descent-SPT Descent-LRPT Descent-EST_SPT Descent-EST_LRPT --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + + // *** Taboo Solvers *** // + // --solver Taboo-EST_LRPT(1,1) --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + // --solver Taboo-EST_LRPT(1,10) Taboo-EST_LRPT(2,10) Taboo-EST_LRPT(3,10) Taboo-EST_LRPT(4,10) Taboo-EST_LRPT(5,10) Taboo-EST_LRPT(6,10) Taboo-EST_LRPT(7,10) Taboo-EST_LRPT(8,10) Taboo-EST_LRPT(9,10) Taboo-EST_LRPT(10,10) --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + // --solver Taboo-EST_LRPT(1,100) Taboo-EST_LRPT(6,100) Taboo-EST_LRPT(8,100) Taboo-EST_LRPT(10,100) Taboo-EST_LRPT(12,100) Taboo-EST_LRPT(14,100) Taboo-EST_LRPT(20,100) Taboo-EST_LRPT(50,100) Taboo-EST_LRPT(100,100) --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + // --solver Taboo-EST_LRPT(1,1000) Taboo-EST_LRPT(6,1000) Taboo-EST_LRPT(8,1000) Taboo-EST_LRPT(10,1000) Taboo-EST_LRPT(12,1000) Taboo-EST_LRPT(14,1000) Taboo-EST_LRPT(20,1000) Taboo-EST_LRPT(50,1000) Taboo-EST_LRPT(100,1000) --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + // --solver Taboo-EST_LRPT(1,5000) Taboo-EST_LRPT(6,5000) Taboo-EST_LRPT(8,5000) Taboo-EST_LRPT(10,5000) Taboo-EST_LRPT(12,5000) Taboo-EST_LRPT(14,5000) Taboo-EST_LRPT(20,5000) Taboo-EST_LRPT(50,5000) Taboo-EST_LRPT(100,5000) --instance aaa1 ft06 ft10 ft20 la01 la02 la03 la04 la05 la06 la07 la08 la09 + + /** All solvers available in this program */ + private static HashMap solvers; + static { + solvers = new HashMap<>(); + solvers.put("basic", new BasicSolver()); + solvers.put("random", new RandomSolver()); + // Add new solvers here + // ******************** Greedy Solver ******************** // + PriorityRule SPT = PriorityRule.SPT; + solvers.put("Greedy-SPT", new GreedySolver(SPT)); + PriorityRule LRPT = PriorityRule.LRPT; + solvers.put("Greedy-LRPT", new GreedySolver(LRPT)); + PriorityESTRule EST_SPT = PriorityESTRule.EST_SPT; + solvers.put("Greedy-EST_SPT", new GreedySolver(EST_SPT)); + PriorityESTRule EST_LRPT = PriorityESTRule.EST_LRPT; + solvers.put("Greedy-EST_LRPT", new GreedySolver(EST_LRPT)); + + // ******************* Descent Solver ******************** // + solvers.put("Descent-SPT", new DescentSolver(SPT)); + solvers.put("Descent-LRPT", new DescentSolver(LRPT)); + solvers.put("Descent-EST_SPT", new DescentSolver(EST_SPT)); + solvers.put("Descent-EST_LRPT", new DescentSolver(EST_LRPT)); + + // ******************** Taboo Solver ********************* // + solvers.put("Taboo-EST_LRPT(1,1)", new TabooSolver(EST_LRPT, 1, 1)); + + solvers.put("Taboo-EST_LRPT(1,10)", new TabooSolver(EST_LRPT, 1, 10)); + solvers.put("Taboo-EST_LRPT(2,10)", new TabooSolver(EST_LRPT, 2, 10)); + solvers.put("Taboo-EST_LRPT(3,10)", new TabooSolver(EST_LRPT, 3, 10)); + solvers.put("Taboo-EST_LRPT(4,10)", new TabooSolver(EST_LRPT, 4, 10)); + solvers.put("Taboo-EST_LRPT(5,10)", new TabooSolver(EST_LRPT, 5, 10)); + solvers.put("Taboo-EST_LRPT(6,10)", new TabooSolver(EST_LRPT, 6, 10)); + solvers.put("Taboo-EST_LRPT(7,10)", new TabooSolver(EST_LRPT, 7, 10)); + solvers.put("Taboo-EST_LRPT(8,10)", new TabooSolver(EST_LRPT, 8, 10)); + solvers.put("Taboo-EST_LRPT(9,10)", new TabooSolver(EST_LRPT, 9, 10)); + solvers.put("Taboo-EST_LRPT(10,10)", new TabooSolver(EST_LRPT, 10, 10)); + + solvers.put("Taboo-EST_LRPT(1,100)", new TabooSolver(EST_LRPT, 1, 100)); + solvers.put("Taboo-EST_LRPT(6,100)", new TabooSolver(EST_LRPT, 6, 100)); + solvers.put("Taboo-EST_LRPT(8,100)", new TabooSolver(EST_LRPT, 8, 100)); + solvers.put("Taboo-EST_LRPT(10,100)", new TabooSolver(EST_LRPT, 10, 100)); + solvers.put("Taboo-EST_LRPT(12,100)", new TabooSolver(EST_LRPT, 12, 100)); + solvers.put("Taboo-EST_LRPT(14,100)", new TabooSolver(EST_LRPT, 14, 100)); + solvers.put("Taboo-EST_LRPT(20,100)", new TabooSolver(EST_LRPT, 20, 100)); + solvers.put("Taboo-EST_LRPT(50,100)", new TabooSolver(EST_LRPT, 50, 100)); + solvers.put("Taboo-EST_LRPT(100,100)", new TabooSolver(EST_LRPT, 100, 100)); + + solvers.put("Taboo-EST_LRPT(1,1000)", new TabooSolver(EST_LRPT, 1, 1000)); + solvers.put("Taboo-EST_LRPT(6,1000)", new TabooSolver(EST_LRPT, 6, 1000)); + solvers.put("Taboo-EST_LRPT(8,1000)", new TabooSolver(EST_LRPT, 8, 1000)); + solvers.put("Taboo-EST_LRPT(10,1000)", new TabooSolver(EST_LRPT, 10, 1000)); + solvers.put("Taboo-EST_LRPT(12,1000)", new TabooSolver(EST_LRPT, 12, 1000)); + solvers.put("Taboo-EST_LRPT(14,1000)", new TabooSolver(EST_LRPT, 14, 1000)); + solvers.put("Taboo-EST_LRPT(20,1000)", new TabooSolver(EST_LRPT, 20, 1000)); + solvers.put("Taboo-EST_LRPT(50,1000)", new TabooSolver(EST_LRPT, 50, 1000)); + solvers.put("Taboo-EST_LRPT(100,1000)", new TabooSolver(EST_LRPT, 100, 1000)); + + solvers.put("Taboo-EST_LRPT(1,5000)", new TabooSolver(EST_LRPT, 1, 5000)); + solvers.put("Taboo-EST_LRPT(6,5000)", new TabooSolver(EST_LRPT, 6, 5000)); + solvers.put("Taboo-EST_LRPT(8,5000)", new TabooSolver(EST_LRPT, 8, 5000)); + solvers.put("Taboo-EST_LRPT(10,5000)", new TabooSolver(EST_LRPT, 10, 5000)); + solvers.put("Taboo-EST_LRPT(12,5000)", new TabooSolver(EST_LRPT, 12, 5000)); + solvers.put("Taboo-EST_LRPT(14,5000)", new TabooSolver(EST_LRPT, 14, 5000)); + solvers.put("Taboo-EST_LRPT(20,5000)", new TabooSolver(EST_LRPT, 20, 5000)); + solvers.put("Taboo-EST_LRPT(50,5000)", new TabooSolver(EST_LRPT, 50, 5000)); + solvers.put("Taboo-EST_LRPT(100,5000)", new TabooSolver(EST_LRPT, 100, 5000)); + } + + + public static void main(String[] args) { + ArgumentParser parser = ArgumentParsers.newFor("jsp-solver").build() + .defaultHelp(true) + .description("Solves jobshop problems."); + + parser.addArgument("-t", "--timeout") + .setDefault(1L) + .type(Long.class) + .help("Solver timeout in seconds for each instance"); + parser.addArgument("--solver") + .nargs("+") + .required(true) + .help("Solver(s) to use (space separated if more than one)"); + + parser.addArgument("--instance") + .nargs("+") + .required(true) + .help("Instance(s) to solve (space separated if more than one)"); + + Namespace ns = null; + try { + ns = parser.parseArgs(args); + } catch (ArgumentParserException e) { + parser.handleError(e); + System.exit(1); + } + + PrintStream output = System.out; + + long solveTimeMs = ns.getLong("timeout") * 1000; + + List solversToTest = ns.getList("solver"); + for(String solverName : solversToTest) { + if(!solvers.containsKey(solverName)) { + System.err.println("ERROR: Solver \"" + solverName + "\" is not avalaible."); + System.err.println(" Available solvers: " + solvers.keySet().toString()); + System.err.println(" You can provide your own solvers by adding them to the `Main.solvers` HashMap."); + System.exit(1); + } + } + List instancePrefixes = ns.getList("instance"); + List instances = new ArrayList<>(); + for(String instancePrefix : instancePrefixes) { + List matches = BestKnownResult.instancesMatching(instancePrefix); + if(matches.isEmpty()) { + System.err.println("ERROR: instance prefix \"" + instancePrefix + "\" does not match any instance."); + System.err.println(" available instances: " + Arrays.toString(BestKnownResult.instances)); + System.exit(1); + } + instances.addAll(matches); + } + + float[] runtimes = new float[solversToTest.size()]; + float[] distances = new float[solversToTest.size()]; + + try { + output.print( " "); + for(String s : solversToTest) + output.printf("%-30s", s); + output.println(); + output.print("instance size best "); + for(String s : solversToTest) { + output.print("runtime makespan ecart "); + } + output.println(); + + + for(String instanceName : instances) { + int bestKnown = BestKnownResult.of(instanceName); + + + Path path = Paths.get("instances/", instanceName); + Instance instance = Instance.fromFile(path); + + output.printf("%-8s %-5s %4d ",instanceName, instance.numJobs +"x"+instance.numTasks, bestKnown); + + for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) { + String solverName = solversToTest.get(solverId); + Solver solver = solvers.get(solverName); + long start = System.currentTimeMillis(); + long deadline = System.currentTimeMillis() + solveTimeMs; + Result result = solver.solve(instance, deadline); + long runtime = System.currentTimeMillis() - start; + + if(!result.schedule.isValid()) { + System.err.println("ERROR: solver returned an invalid schedule"); + System.exit(1); + } + + assert result.schedule.isValid(); + int makespan = result.schedule.makespan(); + float dist = 100f * (makespan - bestKnown) / (float) bestKnown; + runtimes[solverId] += (float) runtime / (float) instances.size(); + distances[solverId] += dist / (float) instances.size(); + + output.printf("%7d %8s %5.1f ", runtime, makespan, dist); + output.flush(); + } + output.println(); + + } + + + output.printf("%-8s %-5s %4s ", "AVG", "-", "-"); + for(int solverId = 0 ; solverId < solversToTest.size() ; solverId++) { + output.printf("%7.1f %8s %5.1f ", runtimes[solverId], "-", distances[solverId]); + } + + + + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/src/main/java/jobshop/Result.java b/src/main/java/jobshop/Result.java index 52b1b8d..7027e7b 100644 --- a/src/main/java/jobshop/Result.java +++ b/src/main/java/jobshop/Result.java @@ -1,23 +1,20 @@ -package jobshop; - -import java.util.Optional; - -@SuppressWarnings("unused") -public class Result { - - public Result(Instance instance, Schedule schedule, ExitCause cause) { - this.instance = instance; - this.schedule = schedule; - this.cause = cause; - } - - public enum ExitCause { - Timeout, ProvedOptimal, Blocked - } - - public final Instance instance; - public final Schedule schedule; - public final ExitCause cause; - - -} +package jobshop; + +public class Result { + + public Result(Instance instance, Schedule schedule, ExitCause cause) { + this.instance = instance; + this.schedule = schedule; + this.cause = cause; + } + + public enum ExitCause { + Timeout, ProvedOptimal, Blocked + } + + public final Instance instance; + public final Schedule schedule; + public final ExitCause cause; + + +} diff --git a/src/main/java/jobshop/Schedule.java b/src/main/java/jobshop/Schedule.java index b29fbf5..5669b89 100644 --- a/src/main/java/jobshop/Schedule.java +++ b/src/main/java/jobshop/Schedule.java @@ -1,157 +1,153 @@ -package jobshop; - - -import java.util.Arrays; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.stream.IntStream; - -import jobshop.encodings.Task; - -public class Schedule { - public final Instance pb; - // start times of each job and task - // times[j][i] is the start time of task (j,i) : i^th task of the j^th job - final int[][] times; - - public Schedule(Instance pb, int[][] times) { - this.pb = pb; - this.times = new int[pb.numJobs][]; - for(int j = 0 ; j < pb.numJobs ; j++) { - this.times[j] = Arrays.copyOf(times[j], pb.numTasks); - } - } - - public int startTime(int job, int task) { - return times[job][task]; - } - - /** Returns true if this schedule is valid (no constraint is violated) */ - public boolean isValid() { - for(int j = 0 ; j startTime(j, t)) - return false; - } - for(int t = 0 ; t path) { - if(startTime(path.get(0)) != 0) { - return false; - } - if(endTime(path.get(path.size()-1)) != makespan()) { - return false; - } - for(int i=0 ; i criticalPath() { - // select task with greatest end time - Task ldd = IntStream.range(0, pb.numJobs) - .mapToObj(j -> new Task(j, pb.numTasks-1)) - .max(Comparator.comparing(this::endTime)) - .get(); - assert endTime(ldd) == makespan(); - - // list that will contain the critical path. - // we construct it from the end, starting with the - // task that finishes last - LinkedList path = new LinkedList<>(); - path.add(0,ldd); - - // keep adding tasks to the path until the first task in the path - // starts a time 0 - while(startTime(path.getFirst()) != 0) { - Task cur = path.getFirst(); - int machine = pb.machine(cur.job, cur.task); - - // will contain the task that was delaying the start - // of our current task - Optional latestPredecessor = Optional.empty(); - - if(cur.task > 0) { - // our current task has a predecessor on the job - Task predOnJob = new Task(cur.job, cur.task -1); - - // if it was the delaying task, save it to predecessor - if(endTime(predOnJob) == startTime(cur)) - latestPredecessor = Optional.of(predOnJob); - } - if(!latestPredecessor.isPresent()) { - // no latest predecessor found yet, look among tasks executing on the same machine - latestPredecessor = IntStream.range(0, pb.numJobs) - .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) - .filter(t -> endTime(t) == startTime(cur)) - .findFirst(); - } - // at this point we should have identified a latest predecessor, either on the job or on the machine - assert latestPredecessor.isPresent() && endTime(latestPredecessor.get()) == startTime(cur); - // insert predecessor at the beginning of the path - path.add(0, latestPredecessor.get()); - } - assert isCriticalPath(path); - return path; - } - - public Schedule copy() { - return new Schedule(this.pb, this.times); - } - - /****************************************************************/ - /* Implémentation de la méthode toString() de la classe Schedule*/ - /****************************************************************/ - public String toString() { - String res = ""; - for (int i = 0; i < this.times.length; i++) { - res += "Job " + Integer.toString(i + 1) + " starting times : \n"; - for (int j = 0; j < this.times[i].length; j++) { - res += "\tTask " + Integer.toString(j + 1) + " starts at time : " + Integer.toString(this.times[i][j]) + "\n"; - } - } - return res; - } -} +package jobshop; + + +import jobshop.encodings.Task; + +import java.util.*; +import java.util.stream.IntStream; + +public class Schedule { + public final Instance pb; + // start times of each job and task + // times[j][i] is the start time of task (j,i) : i^th task of the j^th job + final int[][] times; + + public Schedule(Instance pb, int[][] times) { + this.pb = pb; + this.times = new int[pb.numJobs][]; + for(int j = 0 ; j < pb.numJobs ; j++) { + this.times[j] = Arrays.copyOf(times[j], pb.numTasks); + } + } + + public int startTime(int job, int task) { + return times[job][task]; + } + + /** Returns true if this schedule is valid (no constraint is violated) */ + public boolean isValid() { + for(int j = 0 ; j startTime(j, t)) + return false; + } + for(int t = 0 ; t path) { + if(startTime(path.get(0)) != 0) { + return false; + } + if(endTime(path.get(path.size()-1)) != makespan()) { + return false; + } + for(int i=0 ; i criticalPath() { + // select task with greatest end time + Task ldd = IntStream.range(0, pb.numJobs) + .mapToObj(j -> new Task(j, pb.numTasks-1)) + .max(Comparator.comparing(this::endTime)) + .get(); + assert endTime(ldd) == makespan(); + + // list that will contain the critical path. + // we construct it from the end, starting with the + // task that finishes last + LinkedList path = new LinkedList<>(); + path.add(0,ldd); + + // keep adding tasks to the path until the first task in the path + // starts a time 0 + while(startTime(path.getFirst()) != 0) { + Task cur = path.getFirst(); + int machine = pb.machine(cur.job, cur.task); + + // will contain the task that was delaying the start + // of our current task + Optional latestPredecessor = Optional.empty(); + + if(cur.task > 0) { + // our current task has a predecessor on the job + Task predOnJob = new Task(cur.job, cur.task -1); + + // if it was the delaying task, save it to predecessor + if(endTime(predOnJob) == startTime(cur)) + latestPredecessor = Optional.of(predOnJob); + } + if(!latestPredecessor.isPresent()) { + // no latest predecessor found yet, look among tasks executing on the same machine + latestPredecessor = IntStream.range(0, pb.numJobs) + .mapToObj(j -> new Task(j, pb.task_with_machine(j, machine))) + .filter(t -> endTime(t) == startTime(cur)) + .findFirst(); + } + // at this point we should have identified a latest predecessor, either on the job or on the machine + assert latestPredecessor.isPresent() && endTime(latestPredecessor.get()) == startTime(cur); + // insert predecessor at the beginning of the path + path.add(0, latestPredecessor.get()); + } + assert isCriticalPath(path); + return path; + } + + public Schedule copy() { + return new Schedule(this.pb, this.times); + } + + /****************************************************************/ + /* Implémentation de la méthode toString() de la classe Schedule*/ + /****************************************************************/ + public String toString() { + String res = ""; + for (int i = 0; i < this.times.length; i++) { + res += "Job " + Integer.toString(i + 1) + " starting times : \n"; + for (int j = 0; j < this.times[i].length; j++) { + res += "\tTask " + Integer.toString(j + 1) + " starts at time : " + Integer.toString(this.times[i][j]) + "\n"; + } + } + return res; + } +} diff --git a/src/main/java/jobshop/Solver.java b/src/main/java/jobshop/Solver.java index 61db5ee..20ab271 100644 --- a/src/main/java/jobshop/Solver.java +++ b/src/main/java/jobshop/Solver.java @@ -1,7 +1,7 @@ -package jobshop; - -public interface Solver { - - Result solve(Instance instance, long deadline); - -} +package jobshop; + +public interface Solver { + + Result solve(Instance instance, long deadline); + +} diff --git a/src/main/java/jobshop/encodings/JobNumbers.java b/src/main/java/jobshop/encodings/JobNumbers.java index b94caab..b81f207 100644 --- a/src/main/java/jobshop/encodings/JobNumbers.java +++ b/src/main/java/jobshop/encodings/JobNumbers.java @@ -1,116 +1,84 @@ -package jobshop.encodings; - -import jobshop.Encoding; -import jobshop.Instance; -import jobshop.Schedule; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.stream.IntStream; - -/** Représentation par numéro de job. */ -public class JobNumbers extends Encoding { - - /** A numJobs * numTasks array containing the representation by job numbers. */ - public final int[] jobs; - - /** In case the encoding is only partially filled, indicates the index of first - * element of `jobs` that has not been set yet. */ - public int nextToSet = 0; - - public JobNumbers(Instance instance) { - super(instance); - - jobs = new int[instance.numJobs * instance.numMachines]; - Arrays.fill(jobs, -1); - } - - public JobNumbers(Schedule schedule) { - super(schedule.pb); - - this.jobs = new int[instance.numJobs * instance.numTasks]; - - // for each job indicates which is the next task to be scheduled - int[] nextOnJob = new int[instance.numJobs]; - - while(Arrays.stream(nextOnJob).anyMatch(t -> t < instance.numTasks)) { - Task next = IntStream - // for all jobs numbers - .range(0, instance.numJobs) - // build the next task for this job - .mapToObj(j -> new Task(j, nextOnJob[j])) - // only keep valid tasks (some jobs have no task left to be executed) - .filter(t -> t.task < instance.numTasks) - // select the task with the earliest execution time - .min(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) - .get(); - - this.jobs[nextToSet++] = next.job; - nextOnJob[next.job] += 1; - } - } - - @Override - public Schedule toSchedule() { - // time at which each machine is going to be freed - int[] nextFreeTimeResource = new int[instance.numMachines]; - - // for each job, the first task that has not yet been scheduled - int[] nextTask = new int[instance.numJobs]; - - // for each task, its start time - int[][] startTimes = new int[instance.numJobs][instance.numTasks]; - - // compute the earliest start time for every task of every job - for(int job : jobs) { - int task = nextTask[job]; - int machine = instance.machine(job, task); - // earliest start time for this task - int est = task == 0 ? 0 : startTimes[job][task-1] + instance.duration(job, task-1); - est = Math.max(est, nextFreeTimeResource[machine]); - - startTimes[job][task] = est; - nextFreeTimeResource[machine] = est + instance.duration(job, task); - nextTask[job] = task + 1; - } - - return new Schedule(instance, startTimes); - } - - public static JobNumbers fromSchedule(Schedule sched) { - JobNumbers jo = new JobNumbers(sched.pb); - - int current_time = 0; - Task current_task = new Task(-1,-1); - Task [] done_tasks = new Task[sched.pb.numJobs*sched.pb.numTasks]; - Arrays.fill(done_tasks, current_task); - - int min; - - for (int i = 0; i < sched.pb.numJobs*sched.pb.numTasks; i++) { - // Il faut faire le code ci-dessous autant de fois que l'on a de taches - // On trouve le minimum parmis les restants - min = Integer.MAX_VALUE; - for (int job = 0; job < sched.pb.numJobs; job++) { - for (int task = 0; task < sched.pb.numTasks; task++) { - int task_start_time = sched.startTime(job, task); - Task this_task = new Task(job, task); - if (task_start_time < min && task_start_time >= current_time && !(Arrays.asList(done_tasks).contains(this_task))) { - min = task_start_time; - current_task = this_task; - } - } - } - // Une fois on a trouvé la suivante tache a realiser on introduit le numero du job dans jobs - jo.jobs[jo.nextToSet++] = current_task.job; - done_tasks[i] = current_task; - } - - return jo; - } - - @Override - public String toString() { - return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet)); - } -} +package jobshop.encodings; + +import jobshop.Encoding; +import jobshop.Instance; +import jobshop.Schedule; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.stream.IntStream; + +/** Représentation par numéro de job. */ +public class JobNumbers extends Encoding { + + /** A numJobs * numTasks array containing the representation by job numbers. */ + public final int[] jobs; + + /** In case the encoding is only partially filled, indicates the index of the first + * element of `jobs` that has not been set yet. */ + public int nextToSet = 0; + + public JobNumbers(Instance instance) { + super(instance); + + jobs = new int[instance.numJobs * instance.numMachines]; + Arrays.fill(jobs, -1); + } + + public JobNumbers(Schedule schedule) { + super(schedule.pb); + + this.jobs = new int[instance.numJobs * instance.numTasks]; + + // for each job indicates which is the next task to be scheduled + int[] nextOnJob = new int[instance.numJobs]; + + while(Arrays.stream(nextOnJob).anyMatch(t -> t < instance.numTasks)) { + Task next = IntStream + // for all jobs numbers + .range(0, instance.numJobs) + // build the next task for this job + .mapToObj(j -> new Task(j, nextOnJob[j])) + // only keep valid tasks (some jobs have no task left to be executed) + .filter(t -> t.task < instance.numTasks) + // select the task with the earliest execution time + .min(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) + .get(); + + this.jobs[nextToSet++] = next.job; + nextOnJob[next.job] += 1; + } + } + + @Override + public Schedule toSchedule() { + // time at which each machine is going to be freed + int[] nextFreeTimeResource = new int[instance.numMachines]; + + // for each job, the first task that has not yet been scheduled + int[] nextTask = new int[instance.numJobs]; + + // for each task, its start time + int[][] startTimes = new int[instance.numJobs][instance.numTasks]; + + // compute the earliest start time for every task of every job + for(int job : jobs) { + int task = nextTask[job]; + int machine = instance.machine(job, task); + // earliest start time for this task + int est = task == 0 ? 0 : startTimes[job][task-1] + instance.duration(job, task-1); + est = Math.max(est, nextFreeTimeResource[machine]); + + startTimes[job][task] = est; + nextFreeTimeResource[machine] = est + instance.duration(job, task); + nextTask[job] = task + 1; + } + + return new Schedule(instance, startTimes); + } + + @Override + public String toString() { + return Arrays.toString(Arrays.copyOfRange(jobs,0, nextToSet)); + } +} diff --git a/src/main/java/jobshop/encodings/ResourceOrder.java b/src/main/java/jobshop/encodings/ResourceOrder.java index b115c2d..dfe2c6f 100644 --- a/src/main/java/jobshop/encodings/ResourceOrder.java +++ b/src/main/java/jobshop/encodings/ResourceOrder.java @@ -1,120 +1,126 @@ -package jobshop.encodings; - -import java.util.Comparator; -import java.util.Optional; -import java.util.stream.IntStream; - -import jobshop.Encoding; -import jobshop.Instance; -import jobshop.Schedule; - - -public class ResourceOrder extends Encoding { - - public final Task[][] tasksByMachine; - - public final int[] nextFreeSlot; - - public ResourceOrder(Instance instance) { - super(instance); - - this.tasksByMachine = new Task[instance.numMachines][instance.numJobs]; - for (int i = 0; i < instance.numMachines; i++) { - for (int j = 0; j < instance.numJobs; j++) { - this.tasksByMachine[i][j] = new Task(-1,-1); - } - } - - // no task scheduled on any machine (0 is the default value) - nextFreeSlot = new int[instance.numMachines]; - } - - public ResourceOrder(Schedule schedule) { - super(schedule.pb); - Instance pb = schedule.pb; - - this.tasksByMachine = new Task[pb.numMachines][]; - this.nextFreeSlot = new int[instance.numMachines]; - - for(int m = 0 ; m new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job) - .sorted(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) // sorted by start time - .toArray(Task[]::new); // as new array and store in tasksByMachine - - // indicate that all tasks have been initialized for machine m - nextFreeSlot[m] = instance.numJobs; - } - } - - - @Override - public Schedule toSchedule() { - // indicate for each task that have been scheduled, its start time - int [][] startTimes = new int [instance.numJobs][instance.numTasks]; - - // for each job, how many tasks have been scheduled (0 initially) - int[] nextToScheduleByJob = new int[instance.numJobs]; - - // for each machine, how many tasks have been scheduled (0 initially) - int[] nextToScheduleByMachine = new int[instance.numMachines]; - - // for each machine, earliest time at which the machine can be used - int[] releaseTimeOfMachine = new int[instance.numMachines]; - - - // loop while there remains a job that has unscheduled tasks - while(IntStream.range(0, instance.numJobs).anyMatch(m -> nextToScheduleByJob[m] < instance.numTasks)) { - - // selects a task that has noun scheduled predecessor on its job and machine : - // - it is the next to be schedule on a machine - // - it is the next to be scheduled on its job - // if there is no such task, we have cyclic dependency and the solution is invalid - Optional schedulable = - IntStream.range(0, instance.numMachines) // all machines ... - .filter(m -> nextToScheduleByMachine[m] < instance.numJobs) // ... with unscheduled jobs - .mapToObj(m -> this.tasksByMachine[m][nextToScheduleByMachine[m]]) // tasks that are next to schedule on a machine ... - .filter(task -> task.task == nextToScheduleByJob[task.job]) // ... and on their job - .findFirst(); // select the first one if any - - if(schedulable.isPresent()) { - // we found a schedulable task, lets call it t - Task t = schedulable.get(); - int machine = instance.machine(t.job, t.task); - - // compute the earliest start time (est) of the task - int est = t.task == 0 ? 0 : startTimes[t.job][t.task-1] + instance.duration(t.job, t.task-1); - est = Math.max(est, releaseTimeOfMachine[instance.machine(t.job, t.task)]); - startTimes[t.job][t.task] = est; - - // mark the task as scheduled - nextToScheduleByJob[t.job]++; - nextToScheduleByMachine[machine]++; - // increase the release time of the machine - releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task); - } else { - // no tasks are schedulable, there is no solution for this resource ordering - return null; - } - } - // we exited the loop : all tasks have been scheduled successfully - return new Schedule(instance, startTimes); - } - - @Override - public String toString() { - String res = ""; - for (int i = 0; i < this.tasksByMachine.length; i++) { - res += "Machine number : " + Integer.toString(i+1) + "\n"; - for (int j = 0; j < this.tasksByMachine[i].length; j++) { - res += "\tUse number " + Integer.toString(j+1) + " : " + this.tasksByMachine[i][j].add_one() + "\n"; - } - } - return res; - } - -} +package jobshop.encodings; + +import jobshop.Encoding; +import jobshop.Instance; +import jobshop.Schedule; + +import java.util.Comparator; +import java.util.Optional; +import java.util.stream.IntStream; + +public class ResourceOrder extends Encoding { + + // for each machine m, taskByMachine[m] is an array of tasks to be + // executed on this machine in the same order + public final Task[][] tasksByMachine; + + // for each machine, indicate on many tasks have been initialized + public final int[] nextFreeSlot; + + /** Creates a new empty resource order. */ + public ResourceOrder(Instance instance) + { + super(instance); + + // matrix of null elements (null is the default value of objects) + tasksByMachine = new Task[instance.numMachines][instance.numJobs]; + + // no task scheduled on any machine (0 is the default value) + nextFreeSlot = new int[instance.numMachines]; + } + + /** Creates a resource order from a schedule. */ + public ResourceOrder(Schedule schedule) + { + super(schedule.pb); + Instance pb = schedule.pb; + + this.tasksByMachine = new Task[pb.numMachines][]; + this.nextFreeSlot = new int[instance.numMachines]; + + for(int m = 0 ; m new Task(j, pb.task_with_machine(j, machine))) // all tasks on this machine (one per job) + .sorted(Comparator.comparing(t -> schedule.startTime(t.job, t.task))) // sorted by start time + .toArray(Task[]::new); // as new array and store in tasksByMachine + + // indicate that all tasks have been initialized for machine m + nextFreeSlot[m] = instance.numJobs; + } + } + + @Override + public Schedule toSchedule() { + // indicate for each task that have been scheduled, its start time + int [][] startTimes = new int [instance.numJobs][instance.numTasks]; + + // for each job, how many tasks have been scheduled (0 initially) + int[] nextToScheduleByJob = new int[instance.numJobs]; + + // for each machine, how many tasks have been scheduled (0 initially) + int[] nextToScheduleByMachine = new int[instance.numMachines]; + + // for each machine, earliest time at which the machine can be used + int[] releaseTimeOfMachine = new int[instance.numMachines]; + + + // loop while there remains a job that has unscheduled tasks + while(IntStream.range(0, instance.numJobs).anyMatch(m -> nextToScheduleByJob[m] < instance.numTasks)) { + + // selects a task that has noun scheduled predecessor on its job and machine : + // - it is the next to be schedule on a machine + // - it is the next to be scheduled on its job + // if there is no such task, we have cyclic dependency and the solution is invalid + Optional schedulable = + IntStream.range(0, instance.numMachines) // all machines ... + .filter(m -> nextToScheduleByMachine[m] < instance.numJobs) // ... with unscheduled jobs + .mapToObj(m -> this.tasksByMachine[m][nextToScheduleByMachine[m]]) // tasks that are next to schedule on a machine ... + .filter(task -> task.task == nextToScheduleByJob[task.job]) // ... and on their job + .findFirst(); // select the first one if any + + if(schedulable.isPresent()) { + // we found a schedulable task, lets call it t + Task t = schedulable.get(); + int machine = instance.machine(t.job, t.task); + + // compute the earliest start time (est) of the task + int est = t.task == 0 ? 0 : startTimes[t.job][t.task-1] + instance.duration(t.job, t.task-1); + est = Math.max(est, releaseTimeOfMachine[instance.machine(t)]); + startTimes[t.job][t.task] = est; + + // mark the task as scheduled + nextToScheduleByJob[t.job]++; + nextToScheduleByMachine[machine]++; + // increase the release time of the machine + releaseTimeOfMachine[machine] = est + instance.duration(t.job, t.task); + } else { + // no tasks are schedulable, there is no solution for this resource ordering + return null; + } + } + // we exited the loop : all tasks have been scheduled successfully + return new Schedule(instance, startTimes); + } + + /** Creates an exact copy of this resource order. */ + public ResourceOrder copy() { + return new ResourceOrder(this.toSchedule()); + } + + @Override + public String toString() { + String res = ""; + for (int i = 0; i < this.tasksByMachine.length; i++) { + res += "Machine number : " + Integer.toString(i+1) + "\n"; + for (int j = 0; j < this.tasksByMachine[i].length; j++) { + res += "\tUse number " + Integer.toString(j+1) + " : " + this.tasksByMachine[i][j].add_one() + "\n"; + } + } + return res; + } + +} \ No newline at end of file diff --git a/src/main/java/jobshop/encodings/Task.java b/src/main/java/jobshop/encodings/Task.java index c2d1ed3..0fd8839 100644 --- a/src/main/java/jobshop/encodings/Task.java +++ b/src/main/java/jobshop/encodings/Task.java @@ -1,45 +1,45 @@ -package jobshop.encodings; - -import java.util.Objects; - -/** Represents a task (job,task) of an jobshop problem. - * - * Example : (2, 3) repesents the fourth task of the third job. (remeber that we tart counting at 0) - * */ -public final class Task { - - /** Identifier of the job */ - public final int job; - - /** Index of the task inside the job. */ - public final int task; - - - public Task(int job, int task) { - this.job = job; - this.task = task; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Task task1 = (Task) o; - return job == task1.job && - task == task1.task; - } - - @Override - public int hashCode() { - return Objects.hash(job, task); - } - - @Override - public String toString() { - return "Job " + this.job + " Task " + this.task; - } - - public Task add_one() { - return new Task(this.job + 1, this.task + 1); - } -} +package jobshop.encodings; + +import java.util.Objects; + +/** Represents a task (job,task) of an jobshop problem. + * + * Example : (2, 3) repesents the fourth task of the third job. (remeber that we tart counting at 0) + * */ +public final class Task { + + /** Identifier of the job */ + public final int job; + + /** Index of the task inside the job. */ + public final int task; + + + public Task(int job, int task) { + this.job = job; + this.task = task; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Task task1 = (Task) o; + return job == task1.job && + task == task1.task; + } + + @Override + public int hashCode() { + return Objects.hash(job, task); + } + + @Override + public String toString() { + return "(" + job +", " + task + '}'; + } + + public Task add_one() { + return new Task(this.job + 1, this.task + 1); + } +} diff --git a/src/main/java/jobshop/solvers/BasicSolver.java b/src/main/java/jobshop/solvers/BasicSolver.java index e48fb12..8c1b26e 100644 --- a/src/main/java/jobshop/solvers/BasicSolver.java +++ b/src/main/java/jobshop/solvers/BasicSolver.java @@ -1,21 +1,21 @@ -package jobshop.solvers; - -import jobshop.Instance; -import jobshop.Result; -import jobshop.Solver; -import jobshop.encodings.JobNumbers; - -public class BasicSolver implements Solver { - @Override - public Result solve(Instance instance, long deadline) { - - JobNumbers sol = new JobNumbers(instance); - for(int t = 0 ; t criticalBlockList; + + while(optimizable && deadline > System.currentTimeMillis()) { + // We first take the critical path from the bestSolution + bestResourceOrder = new ResourceOrder(bestSolution); + criticalBlockList = this.blocksOfCriticalPath(bestResourceOrder); + // By default we suppose there will be no optimization possible. If there is, this value will be later changed + optimizable = false; + // We search for the best solution by checking all neighbors + for(Block b : criticalBlockList) { + for(Swap s : this.neighbors(b)) { + // We copy to a variable the bestResourceOrder in order to modified freely while searching for the best solution + currentResourceOrder = bestResourceOrder.copy(); + // We apply the swap on the current Resource Order and we schedule it + s.applyOn(currentResourceOrder); + currentSolution = currentResourceOrder.toSchedule(); + // If the currentSolution duration is smaller than the bestSolution one, save the currentSolution + if(currentSolution != null) { + if(currentSolution.makespan() < bestSolution.makespan()) { + bestSolution = currentSolution; + // While we find better solutions keep running the solve method + optimizable = true; + } + } + } + } + } + // We find the exit cause in order to create the result we will return + ExitCause exitCause = null; + if(deadline <= System.currentTimeMillis()) { + exitCause = ExitCause.Timeout; + } else { + exitCause = ExitCause.ProvedOptimal; + } + + return new Result(instance, bestSolution, exitCause); } - + + // ************************************************************************************************************* // + + + // ************************************************************************************************************* // + // ***************************** blocksOfCriticalPath and neighbors Methods ************************************ // + // ************************************************************************************************************* // /** Returns a list of all blocks of the critical path. */ public List blocksOfCriticalPath(ResourceOrder order) { List criticalBlockList = new ArrayList<>(); - List checkedMachines = new ArrayList<>(); - + Block currentBlock; // Obtain the critical task list from the resource order instance Schedule criticalSchedule = order.toSchedule(); List criticalTaskList = criticalSchedule.criticalPath(); - Block currentBlock; - int currentMachine, m; - int firstTask = 0, lastTask = 0; - Task currentTask; + int totalNumMachines = criticalSchedule.pb.numMachines; + int totalNumJobs = criticalSchedule.pb.numJobs; - System.out.print("Number of Jobs : " + order.instance.numJobs + "\n"); - System.out.print("Number of Tasks : " + order.instance.numTasks + "\n"); - System.out.print("Number of Machines : " + order.instance.numMachines + "\n"); - System.out.print("Critical path : " + criticalTaskList + "\n"); + Task currentTaskRO; + int currentTaskIndexRO, currentCriticalTaskIndex, firstTask, lastTask; - // Initialize the block list - for(int i = 0; i < order.instance.numMachines; i++) { - currentBlock = new Block(i, -1, -1); - criticalBlockList.add(i, currentBlock); - } - - for(int i = 0; i < criticalTaskList.size(); i++) { - currentTask = criticalTaskList.get(i); - currentMachine = order.instance.machine(currentTask.job, currentTask.task); - - // When we find a machine we have not explored, we start searching for all its appearances in the critical path - // and we safe the first and last occurrence of the machine. - if(!checkedMachines.contains(currentMachine)) { - firstTask = 0; - lastTask = 0; - for(int index = i; index < criticalTaskList.size(); index++) { - m = order.instance.machine(criticalTaskList.get(index).job, criticalTaskList.get(index).task); - // If we find a task running in the same machine and it is not the first task, add 1 to the last task - if(currentMachine == m && index > i){ - lastTask++; - } - } - // Add the machine to the checked machines list - checkedMachines.add(currentMachine); - // Create and add the new block to the list - currentBlock = new Block(currentMachine, firstTask, lastTask); - criticalBlockList.set(currentMachine, currentBlock); - } + // We check for all machines + for(int currentMachine = 0; currentMachine < totalNumMachines; currentMachine++) { + currentTaskIndexRO = 0; + while(currentTaskIndexRO < (totalNumJobs-1)){ + currentTaskRO = order.tasksByMachine[currentMachine][currentTaskIndexRO]; + if (criticalTaskList.contains(currentTaskRO)) { + currentCriticalTaskIndex = criticalTaskList.indexOf(currentTaskRO); + //If the next task in the critical path is running in the same machine try find the last task index + if(currentMachine == criticalSchedule.pb.machine(criticalTaskList.get(currentCriticalTaskIndex+1))) { + firstTask = currentTaskIndexRO; + while(currentCriticalTaskIndex < (criticalTaskList.size()-1) && currentMachine == criticalSchedule.pb.machine(criticalTaskList.get(currentCriticalTaskIndex+1))){ + // We advance in the list + currentCriticalTaskIndex++; + // We have to also advance in the resource order + currentTaskIndexRO++; + } + // Create and add the new block to the list + lastTask = currentTaskIndexRO; + currentBlock = new Block(currentMachine, firstTask, lastTask); + criticalBlockList.add(currentBlock); + } + } + // We move on to the next task in the resource order + currentTaskIndexRO++; + } } return criticalBlockList; - } - + } - /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */ - public List neighbors(Block block) { + /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */ + public List neighbors(Block block) { List swapList = new ArrayList<>(); - Swap currentSwap; + Swap swap1, swap2; int machine = block.machine; int firstTask = block.firstTask; int lastTask = block.lastTask; - // Case when there is just one element in the block - if(firstTask == lastTask) { - swapList = null; - } - - for(int i = firstTask; i < lastTask; i++) { - if(i == firstTask + 1) { - currentSwap = new Swap(machine, firstTask, i); - swapList.add(currentSwap); - } - if (i == lastTask - 1) { - currentSwap = new Swap(machine, i, lastTask); - swapList.add(currentSwap); - } - } + // One single swap if there are just 2 elements in the block, two swaps if there are more than 2. + if(firstTask + 1 == lastTask) { + swap1 = new Swap(machine, firstTask, lastTask); + swapList.add(swap1); + } else { + swap1 = new Swap(machine, firstTask, firstTask+1); + swap2 = new Swap(machine, lastTask-1 , lastTask); + swapList.add(swap1); + swapList.add(swap2); + } return swapList; - } - + } + // ************************************************************************************************************* // } diff --git a/src/main/java/jobshop/solvers/GreedySolver.java b/src/main/java/jobshop/solvers/GreedySolver.java index 8de403a..702591d 100644 --- a/src/main/java/jobshop/solvers/GreedySolver.java +++ b/src/main/java/jobshop/solvers/GreedySolver.java @@ -3,9 +3,9 @@ package jobshop.solvers; import java.util.ArrayList; import jobshop.*; +import jobshop.Result.ExitCause; import jobshop.encodings.ResourceOrder; import jobshop.encodings.Task; -import jobshop.solvers.GreedySolver.PriorityESTRule; public class GreedySolver implements Solver { @@ -100,12 +100,12 @@ public class GreedySolver implements Solver { // Search for the date or dates which start sooner ArrayList priorityTasks = new ArrayList<>(); int minStartDate = Integer.MAX_VALUE; - - + Task currentTask; + int currentMachine, currentStartDate; for(int i = 0; i < achievableTasks.size(); i++) { - Task currentTask = achievableTasks.get(i); - int currentMachine = instance.machine(currentTask.job, currentTask.task); - int currentStartDate = Integer.max(nextStartDateJobs[currentTask.job], nextStartDateMachines[currentMachine]); + currentTask = achievableTasks.get(i); + currentMachine = instance.machine(currentTask); + currentStartDate = Integer.max(nextStartDateJobs[currentTask.job], nextStartDateMachines[currentMachine]); if(currentStartDate < minStartDate) { minStartDate = currentStartDate; @@ -124,16 +124,18 @@ public class GreedySolver implements Solver { /********************** Greedy Solver: Constructors + Solve function *************************/ /*********************************************************************************************/ - public PriorityRule priorityRule; - public PriorityESTRule priorityESTRule; + private PriorityRule priorityRule; + private PriorityESTRule priorityESTRule; // 2 constructors: the default and one with the EST restriction public GreedySolver(PriorityRule rule) { + super(); this.priorityRule = rule; this.priorityESTRule = null; } public GreedySolver(PriorityESTRule ruleEST) { + super(); this.priorityESTRule = ruleEST; this.priorityRule = null; } @@ -143,7 +145,7 @@ public class GreedySolver implements Solver { @Override public Result solve(Instance instance, long deadline) { - int currentMachine, currentDuration; + int currentMachine, currentDuration, currentStartDate, nextFreeSlot; // We declare 2 arrays containing the updated moment the next task will start in a job and a machine respectively int[] nextStartDateJobs = new int[instance.numJobs]; int[] nextStartDateMachines = new int[instance.numMachines]; @@ -152,14 +154,13 @@ public class GreedySolver implements Solver { ResourceOrder solution = new ResourceOrder(instance); // Array list with all the achievable current tasks ArrayList achievableTasks = new ArrayList<>(); - + // Initialize the array list with all the first task achievable for(int i = 0 ; i < instance.numJobs ; i++) { - Task currentTask = new Task(i, 0); - achievableTasks.add(currentTask); + achievableTasks.add(new Task(i, 0)); } - while(!achievableTasks.isEmpty()) { + while(!achievableTasks.isEmpty() && deadline > System.currentTimeMillis()) { // We take the task we should do now in function of the priority rule used Task currentTask = null; if(priorityESTRule == null) { @@ -170,14 +171,16 @@ public class GreedySolver implements Solver { System.out.printf("Error priorityRule and priorityRuleEST are null. You must give a value to one of them."); } - // Updating starting dates - currentMachine = instance.machine(currentTask.job, currentTask.task); - currentDuration = instance.duration(currentTask.job, currentTask.task); - nextStartDateJobs[currentTask.job] += currentDuration; - nextStartDateMachines[currentMachine] += currentDuration; - // We remove the current task from the achievable tasks list achievableTasks.remove(currentTask); + + // Updating starting dates + currentMachine = instance.machine(currentTask); + currentDuration = instance.duration(currentTask); + currentStartDate = Integer.max(nextStartDateJobs[currentTask.job], nextStartDateMachines[currentMachine]); + + nextStartDateJobs[currentTask.job] = currentStartDate + currentDuration; + nextStartDateMachines[currentMachine] = currentStartDate + currentDuration; // If it's not the last task of the job, we update the array list with the new task if (currentTask.task < (instance.numTasks - 1)) { @@ -185,10 +188,16 @@ public class GreedySolver implements Solver { } // We add the current task to the solution - int nextFreeSlot = solution.nextFreeSlot[currentMachine]++; + nextFreeSlot = solution.nextFreeSlot[currentMachine]++; solution.tasksByMachine[currentMachine][nextFreeSlot] = currentTask; } - - return new Result(instance, solution.toSchedule(), Result.ExitCause.Blocked); + // We find the exit cause in order to create the result we will return + ExitCause exitCause = null; + if(deadline <= System.currentTimeMillis()) { + exitCause = ExitCause.Timeout; + } else { + exitCause = ExitCause.ProvedOptimal; + } + return new Result(instance, solution.toSchedule(), exitCause); } } diff --git a/src/main/java/jobshop/solvers/RandomSolver.java b/src/main/java/jobshop/solvers/RandomSolver.java index bd649b2..17e2b87 100644 --- a/src/main/java/jobshop/solvers/RandomSolver.java +++ b/src/main/java/jobshop/solvers/RandomSolver.java @@ -1,53 +1,52 @@ -package jobshop.solvers; - -import jobshop.*; -import jobshop.encodings.JobNumbers; - -import java.util.Optional; -import java.util.Random; - -@SuppressWarnings("unused") -public class RandomSolver implements Solver { - - @Override - public Result solve(Instance instance, long deadline) { - Random generator = new Random(0); - - JobNumbers sol = new JobNumbers(instance); - - for(int j = 0 ; j 1) { - shuffleArray(sol.jobs, generator); - Schedule s = sol.toSchedule(); - if(s.makespan() < best.makespan()) { - best = s; - } - } - - - return new Result(instance, best, Result.ExitCause.Timeout); - } - - /** Simple Fisher–Yates array shuffling */ - private static void shuffleArray(int[] array, Random random) - { - int index; - for (int i = array.length - 1; i > 0; i--) - { - index = random.nextInt(i + 1); - if (index != i) - { - array[index] ^= array[i]; - array[i] ^= array[index]; - array[index] ^= array[i]; - } - } - } -} - - +package jobshop.solvers; + +import jobshop.*; +import jobshop.encodings.JobNumbers; + +import java.util.Optional; +import java.util.Random; + +public class RandomSolver implements Solver { + + @Override + public Result solve(Instance instance, long deadline) { + Random generator = new Random(0); + + JobNumbers sol = new JobNumbers(instance); + + for(int j = 0 ; j 1) { + shuffleArray(sol.jobs, generator); + Schedule s = sol.toSchedule(); + if(s.makespan() < best.makespan()) { + best = s; + } + } + + + return new Result(instance, best, Result.ExitCause.Timeout); + } + + /** Simple Fisher–Yates array shuffling */ + private static void shuffleArray(int[] array, Random random) + { + int index; + for (int i = array.length - 1; i > 0; i--) + { + index = random.nextInt(i + 1); + if (index != i) + { + array[index] ^= array[i]; + array[i] ^= array[index]; + array[index] ^= array[i]; + } + } + } +} + + diff --git a/src/main/java/jobshop/solvers/TabooSolver.java b/src/main/java/jobshop/solvers/TabooSolver.java new file mode 100644 index 0000000..5d7fb70 --- /dev/null +++ b/src/main/java/jobshop/solvers/TabooSolver.java @@ -0,0 +1,231 @@ +package jobshop.solvers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import jobshop.Instance; +import jobshop.Result; +import jobshop.Schedule; +import jobshop.Solver; +import jobshop.Result.ExitCause; +import jobshop.encodings.ResourceOrder; +import jobshop.encodings.Task; +import jobshop.solvers.GreedySolver.PriorityESTRule; +import jobshop.solvers.GreedySolver.PriorityRule; +import jobshop.solvers.DescentSolver.*; + + +public class TabooSolver implements Solver { + + private PriorityRule priorityRule; + private PriorityESTRule priorityESTRule; + private int dureeTaboo; + private int maxIter; + + // 2 constructors: the default and one with the EST restriction + public TabooSolver(PriorityRule rule, int dureeTaboo, int maxIter) { + super(); + this.priorityRule = rule; + this.priorityESTRule = null; + this.dureeTaboo = dureeTaboo; + this.maxIter = maxIter; + } + + public TabooSolver(PriorityESTRule ruleEST, int dureeTaboo, int maxIter) { + super(); + this.priorityESTRule = ruleEST; + this.priorityRule = null; + this.dureeTaboo = dureeTaboo; + this.maxIter = maxIter; + } + + + // ************************************************************************************************************* // + // *************************************** TabooSolver: solve Method ******************************************* // + // ************************************************************************************************************* // + + @Override + public Result solve(Instance instance, long deadline) { + // Choosing rule (SPT / LRPT / EST_SPT / EST_LRPT) + GreedySolver greedy = null; + if(priorityESTRule == null) { + PriorityRule currentRule = this.priorityRule; + greedy = new GreedySolver(currentRule); + } else if(priorityRule == null) { + PriorityESTRule currentESTRule = this.priorityESTRule; + greedy = new GreedySolver(currentESTRule); + } else { + System.out.printf("Error priorityRule and priorityRuleEST are null. You must give a value to one of them."); + } + + // Generating a viable solution + Result result = greedy.solve(instance, deadline); + Schedule initialSolution = result.schedule; + ResourceOrder initialResourceOrder = new ResourceOrder(initialSolution); + + // Declaring all solution types + ResourceOrder bestRO = initialResourceOrder; // s* + ResourceOrder currentRO = bestRO.copy(); // s + ResourceOrder bestNeighborRO = bestRO.copy(); // s' + ResourceOrder neighborRO; // s'' + + // Defining the sTaboo variables + int totalTasks = instance.numJobs * instance.numTasks; + int[][] sTaboo = new int[totalTasks][totalTasks]; + // Initializing sTaboo with all 0 + for (int[] row : sTaboo) { + Arrays.fill(row, 0); + } + + // Declaring other variables + List criticalBlockList; + int TASK_PER_JOB, j1, i1, j2, i2, taskID1, taskID2, forbiddenTaskID1, forbiddenTaskID2; + int bestMakespan = initialSolution.makespan(); + int bestNeighborMakespan, neighborMakespan; + boolean updated; + + // Iteration Counter + int k = 0; + + while (deadline > System.currentTimeMillis() && k <= this.maxIter) { + // ***************** 1. k <- k + 1 ******************************************************** // + k++; + + // ***************** 2. Choose the best neighbor s' that is not in sTaboo ***************** // + bestNeighborMakespan = Integer.MAX_VALUE; + forbiddenTaskID1 = -1; + forbiddenTaskID2 = -1; + updated = false; + + // We first take the critical path from the currentRO (s) + currentRO = bestNeighborRO.copy(); // (s <- s') + criticalBlockList = this.blocksOfCriticalPath(currentRO); + + for(Block b : criticalBlockList) { + for(Swap s : neighbors(b)) { + // Extract the current index values for sTaboo + TASK_PER_JOB = currentRO.instance.numTasks; + j1 = currentRO.tasksByMachine[s.machine][s.t1].job; + j2 = currentRO.tasksByMachine[s.machine][s.t2].job; + i1 = currentRO.tasksByMachine[s.machine][s.t1].task; + i2 = currentRO.tasksByMachine[s.machine][s.t2].task; + taskID1 = j1 * TASK_PER_JOB + i1; + taskID2 = j2 * TASK_PER_JOB + i2; + + // Check if it is a forbidden swap + if(sTaboo[taskID1][taskID2] < k) { + updated= true; + neighborRO = currentRO.copy(); + // We apply the swap on the current Resource Order and we schedule it to find its makespan + s.applyOn(neighborRO); + neighborMakespan = neighborRO.toSchedule().makespan(); + + if(neighborMakespan < bestNeighborMakespan) { + // We forbid the opposite permutation of the given tasks (in index taskID1 and taskID2) + forbiddenTaskID1 = taskID1; + forbiddenTaskID2 = taskID2; + // We have checked all neighbors and we have chosen the best one: bestNeighborRO + bestNeighborMakespan = neighborMakespan; + bestNeighborRO = neighborRO.copy(); + } + } + } + } + // ************************ 3. Add bestNeighborSolution (s') to sTaboo *************************** // + // If it is not updated it means all solutions are forbidden + if(updated) { + sTaboo[forbiddenTaskID2][forbiddenTaskID1] = this.dureeTaboo + k; + // ******************** 4. If s' is better than s* then s* <- s' ************************** // + if(bestNeighborMakespan < bestMakespan) { + bestMakespan = bestNeighborMakespan; + bestRO = bestNeighborRO.copy(); + } + } + } + // We find the exit cause in order to create the result we will return + ExitCause exitCause = null; + if(deadline <= System.currentTimeMillis()) { + exitCause = ExitCause.Timeout; + } else if(k >= this.maxIter) { + exitCause = ExitCause.Blocked; + } else { + exitCause = ExitCause.ProvedOptimal; + } + return new Result(instance, bestRO.toSchedule(), exitCause); + } + // ************************************************************************************************************* // + + + // ************************************************************************************************************* // + // ********************************** Copied functions from DescentSolver ************************************** // + // ************************************************************************************************************* // + + /** Returns a list of all blocks of the critical path. */ + private List blocksOfCriticalPath(ResourceOrder order) { + List criticalBlockList = new ArrayList<>(); + Block currentBlock; + // Obtain the critical task list from the resource order instance + Schedule criticalSchedule = order.toSchedule(); + List criticalTaskList = criticalSchedule.criticalPath(); + + int totalNumMachines = criticalSchedule.pb.numMachines; + int totalNumJobs = criticalSchedule.pb.numJobs; + + Task currentTaskRO; + int currentTaskIndexRO, currentCriticalTaskIndex, firstTask, lastTask; + + // We check for all machines + for(int currentMachine = 0; currentMachine < totalNumMachines; currentMachine++) { + currentTaskIndexRO = 0; + while(currentTaskIndexRO < (totalNumJobs-1)){ + currentTaskRO = order.tasksByMachine[currentMachine][currentTaskIndexRO]; + if (criticalTaskList.contains(currentTaskRO)) { + currentCriticalTaskIndex = criticalTaskList.indexOf(currentTaskRO); + //If the next task in the critical path is running in the same machine try find the last task index + if(currentMachine == criticalSchedule.pb.machine(criticalTaskList.get(currentCriticalTaskIndex+1))) { + firstTask = currentTaskIndexRO; + while(currentCriticalTaskIndex < (criticalTaskList.size()-1) && currentMachine == criticalSchedule.pb.machine(criticalTaskList.get(currentCriticalTaskIndex+1))){ + // We advance in the list + currentCriticalTaskIndex++; + // We have to also advance in the resource order + currentTaskIndexRO++; + } + // Create and add the new block to the list + lastTask = currentTaskIndexRO; + currentBlock = new Block(currentMachine, firstTask, lastTask); + criticalBlockList.add(currentBlock); + } + } + // We move on to the next task in the resource order + currentTaskIndexRO++; + } + } + return criticalBlockList; + } + + /** For a given block, return the possible swaps for the Nowicki and Smutnicki neighborhood */ + private List neighbors(Block block) { + List swapList = new ArrayList<>(); + Swap swap1; + Swap swap2; + + int machine = block.machine; + int firstTask = block.firstTask; + int lastTask = block.lastTask; + + // One single swap if there are just 2 elements in the block, two swaps if there are more than 2. + if(firstTask + 1 == lastTask) { + swap1 = new Swap(machine, firstTask, lastTask); + swapList.add(swap1); + } else { + swap1 = new Swap(machine, firstTask, firstTask+1); + swap2 = new Swap(machine, lastTask-1 , lastTask); + swapList.add(swap1); + swapList.add(swap2); + } + return swapList; + } + + // ************************************************************************************************************* // +} diff --git a/src/test/java/jobshop/encodings/EncodingTests.java b/src/test/java/jobshop/encodings/EncodingTests.java index a04eeeb..029fc71 100644 --- a/src/test/java/jobshop/encodings/EncodingTests.java +++ b/src/test/java/jobshop/encodings/EncodingTests.java @@ -1,103 +1,75 @@ -package jobshop.encodings; - -import jobshop.Instance; -import jobshop.Result; -import jobshop.Schedule; -import jobshop.Solver; -import jobshop.solvers.BasicSolver; -import jobshop.solvers.GreedySolver; -import jobshop.solvers.GreedySolver.PriorityRule; - -import org.junit.Test; - -import java.io.IOException; -import java.nio.file.Paths; - -public class EncodingTests { - - @Test - public void testJobNumbers() throws IOException { - Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); - - // numéro de jobs : 1 2 2 1 1 2 (cf exercices) - JobNumbers enc = new JobNumbers(instance); - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - - Schedule sched = enc.toSchedule(); - // TODO: make it print something meaningful - // by implementing the toString() method - System.out.println(sched); - assert sched.isValid(); - assert sched.makespan() == 12; - - - - // numéro de jobs : 1 1 2 2 1 2 - enc = new JobNumbers(instance); - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - - sched = enc.toSchedule(); - assert sched.isValid(); - assert sched.makespan() == 14; - } - - @Test - public void testBasicSolver() throws IOException { - Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); - - // build a solution that should be equal to the result of BasicSolver - JobNumbers enc = new JobNumbers(instance); - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - - Schedule sched = enc.toSchedule(); - assert sched.isValid(); - assert sched.makespan() == 12; - - Solver solver = new BasicSolver(); - Result result = solver.solve(instance, System.currentTimeMillis() + 10); - - assert result.schedule.isValid(); - assert result.schedule.makespan() == sched.makespan(); // should have the same makespan - } - - @Test - public void testGreedySolver() throws IOException { - Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); - - // build a solution that should be equal to the result of BasicSolver - JobNumbers enc = new JobNumbers(instance); - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - enc.jobs[enc.nextToSet++] = 0; - enc.jobs[enc.nextToSet++] = 1; - - Schedule sched = enc.toSchedule(); - assert sched.isValid(); - assert sched.makespan() == 12; - - PriorityRule priorityRule = PriorityRule.SPT; - Solver solver = new GreedySolver(priorityRule); - Result result = solver.solve(instance, System.currentTimeMillis() + 10); - - assert result.schedule.isValid(); - assert result.schedule.makespan() == sched.makespan(); // should have the same makespan - } - -} +package jobshop.encodings; + +import jobshop.Instance; +import jobshop.Result; +import jobshop.Schedule; +import jobshop.Solver; +import jobshop.solvers.BasicSolver; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Paths; + +public class EncodingTests { + + @Test + public void testJobNumbers() throws IOException { + Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); + + // numéro de jobs : 1 2 2 1 1 2 (cf exercices) + JobNumbers enc = new JobNumbers(instance); + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + + Schedule sched = enc.toSchedule(); + // TODO: make it print something meaningful + // by implementing the toString() method + System.out.println(sched); + assert sched.isValid(); + assert sched.makespan() == 12; + + + + // numéro de jobs : 1 1 2 2 1 2 + enc = new JobNumbers(instance); + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + + sched = enc.toSchedule(); + assert sched.isValid(); + assert sched.makespan() == 14; + } + + @Test + public void testBasicSolver() throws IOException { + Instance instance = Instance.fromFile(Paths.get("instances/aaa1")); + + // build a solution that should be equal to the result of BasicSolver + JobNumbers enc = new JobNumbers(instance); + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + enc.jobs[enc.nextToSet++] = 0; + enc.jobs[enc.nextToSet++] = 1; + + Schedule sched = enc.toSchedule(); + assert sched.isValid(); + assert sched.makespan() == 12; + + Solver solver = new BasicSolver(); + Result result = solver.solve(instance, System.currentTimeMillis() + 10); + + assert result.schedule.isValid(); + assert result.schedule.makespan() == sched.makespan(); // should have the same makespan + } + +}