job-shop-lib 0.5.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. job_shop_lib/__init__.py +19 -8
  2. job_shop_lib/{base_solver.py → _base_solver.py} +1 -1
  3. job_shop_lib/{job_shop_instance.py → _job_shop_instance.py} +155 -81
  4. job_shop_lib/_operation.py +118 -0
  5. job_shop_lib/{schedule.py → _schedule.py} +102 -84
  6. job_shop_lib/{scheduled_operation.py → _scheduled_operation.py} +25 -49
  7. job_shop_lib/benchmarking/__init__.py +66 -43
  8. job_shop_lib/benchmarking/_load_benchmark.py +88 -0
  9. job_shop_lib/constraint_programming/__init__.py +13 -0
  10. job_shop_lib/{cp_sat/ortools_solver.py → constraint_programming/_ortools_solver.py} +77 -22
  11. job_shop_lib/dispatching/__init__.py +51 -42
  12. job_shop_lib/dispatching/{dispatcher.py → _dispatcher.py} +223 -130
  13. job_shop_lib/dispatching/_dispatcher_observer_config.py +67 -0
  14. job_shop_lib/dispatching/_factories.py +135 -0
  15. job_shop_lib/dispatching/{history_tracker.py → _history_observer.py} +6 -7
  16. job_shop_lib/dispatching/_optimal_operations_observer.py +113 -0
  17. job_shop_lib/dispatching/_ready_operation_filters.py +168 -0
  18. job_shop_lib/dispatching/_unscheduled_operations_observer.py +70 -0
  19. job_shop_lib/dispatching/feature_observers/__init__.py +51 -13
  20. job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +212 -0
  21. job_shop_lib/dispatching/feature_observers/{duration_observer.py → _duration_observer.py} +20 -18
  22. job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +289 -0
  23. job_shop_lib/dispatching/feature_observers/_factory.py +95 -0
  24. job_shop_lib/dispatching/feature_observers/_feature_observer.py +228 -0
  25. job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +97 -0
  26. job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +35 -0
  27. job_shop_lib/dispatching/feature_observers/{is_scheduled_observer.py → _is_scheduled_observer.py} +9 -5
  28. job_shop_lib/dispatching/feature_observers/{position_in_job_observer.py → _position_in_job_observer.py} +8 -10
  29. job_shop_lib/dispatching/feature_observers/{remaining_operations_observer.py → _remaining_operations_observer.py} +8 -26
  30. job_shop_lib/dispatching/rules/__init__.py +87 -0
  31. job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +84 -0
  32. job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +201 -0
  33. job_shop_lib/dispatching/{dispatching_rules.py → rules/_dispatching_rules_functions.py} +70 -16
  34. job_shop_lib/dispatching/rules/_machine_chooser_factory.py +71 -0
  35. job_shop_lib/dispatching/rules/_utils.py +128 -0
  36. job_shop_lib/exceptions.py +18 -0
  37. job_shop_lib/generation/__init__.py +19 -0
  38. job_shop_lib/generation/_general_instance_generator.py +165 -0
  39. job_shop_lib/generation/_instance_generator.py +133 -0
  40. job_shop_lib/{generators/transformations.py → generation/_transformations.py} +16 -12
  41. job_shop_lib/generation/_utils.py +124 -0
  42. job_shop_lib/graphs/__init__.py +30 -12
  43. job_shop_lib/graphs/{build_disjunctive_graph.py → _build_disjunctive_graph.py} +41 -3
  44. job_shop_lib/graphs/{build_agent_task_graph.py → _build_resource_task_graphs.py} +28 -26
  45. job_shop_lib/graphs/_constants.py +38 -0
  46. job_shop_lib/graphs/_job_shop_graph.py +320 -0
  47. job_shop_lib/graphs/_node.py +182 -0
  48. job_shop_lib/graphs/graph_updaters/__init__.py +26 -0
  49. job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py +108 -0
  50. job_shop_lib/graphs/graph_updaters/_graph_updater.py +57 -0
  51. job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +155 -0
  52. job_shop_lib/graphs/graph_updaters/_utils.py +25 -0
  53. job_shop_lib/py.typed +0 -0
  54. job_shop_lib/reinforcement_learning/__init__.py +68 -0
  55. job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +398 -0
  56. job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py +329 -0
  57. job_shop_lib/reinforcement_learning/_reward_observers.py +87 -0
  58. job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +443 -0
  59. job_shop_lib/reinforcement_learning/_types_and_constants.py +62 -0
  60. job_shop_lib/reinforcement_learning/_utils.py +199 -0
  61. job_shop_lib/visualization/__init__.py +0 -25
  62. job_shop_lib/visualization/gantt/__init__.py +48 -0
  63. job_shop_lib/visualization/gantt/_gantt_chart_creator.py +257 -0
  64. job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py +422 -0
  65. job_shop_lib/visualization/{gantt_chart.py → gantt/_plot_gantt_chart.py} +84 -21
  66. job_shop_lib/visualization/graphs/__init__.py +29 -0
  67. job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py +418 -0
  68. job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +389 -0
  69. {job_shop_lib-0.5.0.dist-info → job_shop_lib-1.0.0.dist-info}/METADATA +87 -55
  70. job_shop_lib-1.0.0.dist-info/RECORD +73 -0
  71. {job_shop_lib-0.5.0.dist-info → job_shop_lib-1.0.0.dist-info}/WHEEL +1 -1
  72. job_shop_lib/benchmarking/load_benchmark.py +0 -142
  73. job_shop_lib/cp_sat/__init__.py +0 -5
  74. job_shop_lib/dispatching/dispatching_rule_solver.py +0 -119
  75. job_shop_lib/dispatching/factories.py +0 -206
  76. job_shop_lib/dispatching/feature_observers/composite_feature_observer.py +0 -87
  77. job_shop_lib/dispatching/feature_observers/earliest_start_time_observer.py +0 -156
  78. job_shop_lib/dispatching/feature_observers/factory.py +0 -58
  79. job_shop_lib/dispatching/feature_observers/feature_observer.py +0 -113
  80. job_shop_lib/dispatching/feature_observers/is_completed_observer.py +0 -98
  81. job_shop_lib/dispatching/feature_observers/is_ready_observer.py +0 -40
  82. job_shop_lib/dispatching/pruning_functions.py +0 -116
  83. job_shop_lib/generators/__init__.py +0 -7
  84. job_shop_lib/generators/basic_generator.py +0 -197
  85. job_shop_lib/graphs/constants.py +0 -21
  86. job_shop_lib/graphs/job_shop_graph.py +0 -202
  87. job_shop_lib/graphs/node.py +0 -166
  88. job_shop_lib/operation.py +0 -122
  89. job_shop_lib/visualization/agent_task_graph.py +0 -257
  90. job_shop_lib/visualization/create_gif.py +0 -209
  91. job_shop_lib/visualization/disjunctive_graph.py +0 -210
  92. job_shop_lib-0.5.0.dist-info/RECORD +0 -48
  93. {job_shop_lib-0.5.0.dist-info → job_shop_lib-1.0.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,73 @@
1
+ job_shop_lib/__init__.py,sha256=o3TWKp2_4n2WquWkaCsMo32Dd3TkXRRaPIEKpvgiI2k,639
2
+ job_shop_lib/_base_solver.py,sha256=p17XmtufNc9Y481cqZUT45pEkUmmW1HWG53dfhIBJH8,1363
3
+ job_shop_lib/_job_shop_instance.py,sha256=hNQGSJj0rEQpS-YhzwWmM6QzCWp6r--89jkghSgLvUs,18380
4
+ job_shop_lib/_operation.py,sha256=DrnlIrmnKFT5lzMvqCMszF18WcK77AJTRDO5wWrmm7s,4273
5
+ job_shop_lib/_schedule.py,sha256=-RdCtTTj-SdNLFucmSVnrCbjZLcBZ4yfhRBdATjAaW8,11292
6
+ job_shop_lib/_scheduled_operation.py,sha256=czrGr87EOTlO2NPolIN5CDigeiCzvQEyra5IZPwSFZc,2801
7
+ job_shop_lib/benchmarking/__init__.py,sha256=BYCrJUNr_uk2c0xIbDt07OnUMhQx8Dudkukx3TFWxgw,3271
8
+ job_shop_lib/benchmarking/_load_benchmark.py,sha256=Jb6HYGKkub-3uU3l3NreRPE0PU6f0n8G9Mih5vMImOI,2936
9
+ job_shop_lib/benchmarking/benchmark_instances.json,sha256=F9EvyzFwVxiKAN6rQTsrMhsKstmyUmroyWduM7a00KQ,464841
10
+ job_shop_lib/constraint_programming/__init__.py,sha256=kKQRUxxS_nVFUdXGnf4bQOD9mqrXxZZWElS753A4YiA,454
11
+ job_shop_lib/constraint_programming/_ortools_solver.py,sha256=vz_Kg_CmvZ13yGgqi-hZuFkosJR1v449xNaAZV3PhsE,10501
12
+ job_shop_lib/dispatching/__init__.py,sha256=SXVd0Zh6xTp-lNT7c463pii3l168NCZYf-5uOwBI1Fc,1770
13
+ job_shop_lib/dispatching/_dispatcher.py,sha256=oC-1h6p83qzumynJWMzsrbsudM1tt9AebAufKEjygRI,22039
14
+ job_shop_lib/dispatching/_dispatcher_observer_config.py,sha256=RaUkLxYCHG8Tx2tPgFyOBa8FAcbREZdKuTyLsyaYvhA,2473
15
+ job_shop_lib/dispatching/_factories.py,sha256=NzpUdxHDU_aVjHBScu8HVhSKZnTKYItHcUFS4mUp4KM,4723
16
+ job_shop_lib/dispatching/_history_observer.py,sha256=dixJe83quzGNwG0u0k2uES7GsLw0zWCjX0MOUD4VTRU,634
17
+ job_shop_lib/dispatching/_optimal_operations_observer.py,sha256=HS933mn2VlwgE7plSsth94RGlbEzfskF6TNyOM7rQrY,4246
18
+ job_shop_lib/dispatching/_ready_operation_filters.py,sha256=vzo9vfijhc-Y75GrBpxuoYKaUuSL7-picD230PdhwuI,5778
19
+ job_shop_lib/dispatching/_unscheduled_operations_observer.py,sha256=3E0ePesDdWdNs6520znnOBW3eiegJj5bZg9Tmb0xoSA,2705
20
+ job_shop_lib/dispatching/feature_observers/__init__.py,sha256=EuJLvSpJpoXUK8A4UuC2k6Mpa293ZR3oCnnvYivIBtU,2240
21
+ job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py,sha256=QqgadgmHjNTPOKs4wVamnPjqls9kKOH40o3N0lZZ4aw,8026
22
+ job_shop_lib/dispatching/feature_observers/_duration_observer.py,sha256=fbkUIVScF1iNjdVCYr1ImQm53TfahvVnGXhsRAsgdzY,4129
23
+ job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py,sha256=W5Tr81Kme8N-m85jmX7yVc65_xlwNQBvVjnjlL-aq7w,11493
24
+ job_shop_lib/dispatching/feature_observers/_factory.py,sha256=RWrRdEDBv_0vpJNHD67_Qjov2_72yS5d7tXJiWuyobQ,3321
25
+ job_shop_lib/dispatching/feature_observers/_feature_observer.py,sha256=nxDPYoVu5nAeDuXqdkA-H8slHRSt47JyHDERNachDCA,8693
26
+ job_shop_lib/dispatching/feature_observers/_is_completed_observer.py,sha256=8nmdnlPQfuudipWcxPhUPbQvlIHLmnzoeQyWkNzB-J8,3642
27
+ job_shop_lib/dispatching/feature_observers/_is_ready_observer.py,sha256=aP5CpwmCWP4w8J69qAC7QwGRQGMlfNbM31n-BRu92DA,1289
28
+ job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py,sha256=OcuMUB9_By6ZMtX-1_3z-xaxGbP85a5Zv0ywAv7XxWQ,1491
29
+ job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py,sha256=WRknpQBKXs6h6cXLFJW7ZCvjtU8CPL-iXXNPw3g-mLE,1303
30
+ job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py,sha256=5V87lCrJUabEe8AkTGXPu5yS8OGxeN8L3-xNyHmdmLs,1441
31
+ job_shop_lib/dispatching/rules/__init__.py,sha256=g3PGvMLMa3WMgNhGSW3S_xkHqoHpW8hr_9JqOfR7Xrk,2140
32
+ job_shop_lib/dispatching/rules/_dispatching_rule_factory.py,sha256=0v7IcSQadvlX6tRy86Z55ruwIY70H9q9E46tdazjtkU,2942
33
+ job_shop_lib/dispatching/rules/_dispatching_rule_solver.py,sha256=9-UE0HiHCeFXFGqB85cSfduLCEm5k5bJkmIujP-_irg,7321
34
+ job_shop_lib/dispatching/rules/_dispatching_rules_functions.py,sha256=wfBdiKqEQQ8C5Gg_mrWWSuWncPwUkFacjeAQ8D4n9Wc,7648
35
+ job_shop_lib/dispatching/rules/_machine_chooser_factory.py,sha256=AtYJGuvKlc3T4Y5NCGxgjQ-np3d1aeADAZ3r68No_WA,2383
36
+ job_shop_lib/dispatching/rules/_utils.py,sha256=DFDpRoHb56Rtn01vfN69Bq0X3F8P1EtM6trHx9aXg3U,4643
37
+ job_shop_lib/exceptions.py,sha256=ARzpoZJCvRIvOesCiqqFSRxkv6w9WwEXx0aBP-l2IKA,1597
38
+ job_shop_lib/generation/__init__.py,sha256=tgMVhnh62lkwGKywvingFD9SLhc-vERKiWsS-41qQKA,605
39
+ job_shop_lib/generation/_general_instance_generator.py,sha256=e-NDkH-NoCwa14oADj6n_I7BX5xWWVVzRLvb4rpJ92w,6374
40
+ job_shop_lib/generation/_instance_generator.py,sha256=VV0OKX4JgFq3I1EY6s3LrOdPjM3v4lH6S1hkUebTkFQ,4615
41
+ job_shop_lib/generation/_transformations.py,sha256=X-hTAJVIHZ3bmF1rqS0zCit8r5SGpHpV8Fcl92fejow,5336
42
+ job_shop_lib/generation/_utils.py,sha256=cBhGILE0FE3TqvWoHqpaFEffO8D2fb869pF-BdMlYsg,3617
43
+ job_shop_lib/graphs/__init__.py,sha256=anR7zg1eHf1JweRGFbrwE26MeFbzQYBEHTfy6osalyU,1897
44
+ job_shop_lib/graphs/_build_disjunctive_graph.py,sha256=UbUYdeQaaeEqLchcKJGHEFGl4wElfGLb1o_R-u8wqnA,5120
45
+ job_shop_lib/graphs/_build_resource_task_graphs.py,sha256=mWg8C-62aqvAwIKsreAHLYIq-VOc0q7BEnOnlUrywb8,6961
46
+ job_shop_lib/graphs/_constants.py,sha256=K-GeVvh_DTWpo1KOX1clmxWS_pkUJbq19yOBmrCVIxI,1086
47
+ job_shop_lib/graphs/_job_shop_graph.py,sha256=TdpUNLv9FBuosPrhLrhQl75u_kwRX0vygKFknLT6pJY,11480
48
+ job_shop_lib/graphs/_node.py,sha256=9TFH8C1D44W1IvOIG8MucLNQyLzasyBXVkMZTJU4rso,6075
49
+ job_shop_lib/graphs/graph_updaters/__init__.py,sha256=VpFsGPzm4VQiXE0vBeAL5h3jXxiR7Hap0F2BPQwRTjQ,639
50
+ job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py,sha256=-t0T8W-Jz9TJQR9-ljPkcDsDC4CwJAfs2nUF3zjEtuw,4369
51
+ job_shop_lib/graphs/graph_updaters/_graph_updater.py,sha256=j1f7iWsa62GVszK2BPaMxnKBCEGWa9owm8g4VWUje8w,1967
52
+ job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py,sha256=SyQJXIJvXijO51AzPz7YbCPZZK8d8JHE63LFX_F95Gc,6102
53
+ job_shop_lib/graphs/graph_updaters/_utils.py,sha256=sdw2Vo75P9c6Fy-YBlfgpXb9gPwHUluTB1E-9WINm_g,730
54
+ job_shop_lib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
+ job_shop_lib/reinforcement_learning/__init__.py,sha256=CZOnKqyjfcPXOh7ZLy-RvNbIAi1SnUs0WGC1fHo09pE,1512
56
+ job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py,sha256=fwgKveAoNtO-uV4NvmfSG_ZeDPY0W2KZYcsFNHY8QY4,15749
57
+ job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py,sha256=4H53fKMgxPWqFVF5WczomEcrFdq7abBgHnOYJ1m6D1c,12768
58
+ job_shop_lib/reinforcement_learning/_reward_observers.py,sha256=iWHccnujeAKyTQn2ilQ4BhcEccoSTyJqQ5yOiP5GG_Y,2984
59
+ job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py,sha256=FCghBwfKZZg2VFJdV0ihzRJBTwMuivB-zhf9qM2OZq8,16803
60
+ job_shop_lib/reinforcement_learning/_types_and_constants.py,sha256=6FpuQkZLV2H8_dXmax49OTgAw7dWQcUEWVWWdMLR7bs,1752
61
+ job_shop_lib/reinforcement_learning/_utils.py,sha256=0jM7qPCxYbURKAQlCLt4Ah1OrmdGUUhxhOsszTdt2Zk,6049
62
+ job_shop_lib/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
+ job_shop_lib/visualization/gantt/__init__.py,sha256=HGXwRgDuMAldqU0JBdiZCd5e79XBz1r96qHeDVlzE54,1145
64
+ job_shop_lib/visualization/gantt/_gantt_chart_creator.py,sha256=LTsVhpB1Fb_2o08HRZPPXSekwzR7fyTSC6h549XMqhU,8638
65
+ job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py,sha256=KeMiTBOtJKchnKGD4av8_3x3S5h437pRymc2q2knbNc,14617
66
+ job_shop_lib/visualization/gantt/_plot_gantt_chart.py,sha256=9-NSSNsVcW8gYLZtAuFeYURqi8cHNkVYufosKtbKFOI,6881
67
+ job_shop_lib/visualization/graphs/__init__.py,sha256=282hZFg07EyQu4HVt4GzFfYnY6ZF376IMjnWZ5eg0ZQ,611
68
+ job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py,sha256=wF2zaPsvg1TszP_2n3ialTTUS7IkCqu9y79kU0bGbpw,15982
69
+ job_shop_lib/visualization/graphs/_plot_resource_task_graph.py,sha256=RgJqHS5hJh3KkyaLbtpG_bER981BFRwGpflz7I7gS64,13271
70
+ job_shop_lib-1.0.0.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
71
+ job_shop_lib-1.0.0.dist-info/METADATA,sha256=PHGguy8Da24M4rqmxcAxkGmct54ZFVgsw5NXEBm-pAw,16110
72
+ job_shop_lib-1.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
73
+ job_shop_lib-1.0.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.8.1
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,142 +0,0 @@
1
- """Module for loading benchmark instances.
2
-
3
- All benchmark instances are stored in a single JSON file. This module provides
4
- functions to load the instances from the file and return them as
5
- JobShopInstance objects.
6
-
7
- The contributions to this benchmark dataset are as follows:
8
-
9
- abz5-9: This subset, comprising five instances, was introduced by Adams et
10
- al. (1988).
11
- ft06, ft10, ft20: These three instances are attributed to the work of
12
- Fisher and Thompson, as detailed in their 1963 work.
13
- la01-40: A collection of forty instances, this group was contributed by
14
- Lawrence, as referenced in his 1984 report.
15
- orb01-10: Ten instances in this category were provided by Applegate and
16
- Cook, as seen in their 1991 study.
17
- swb01-20: This segment, encompassing twenty instances, was contributed by
18
- Storer et al., as per their 1992 article.
19
- yn1-4: Yamada and Nakano are credited with the addition of four instances
20
- in this group, as found in their 1992 paper.
21
- ta01-80: The largest contribution, consisting of eighty instances, was
22
- made by Taillard, as documented in his 1993 paper.
23
-
24
- The metadata from these instances has been updated using data from:
25
-
26
- Thomas Weise. jsspInstancesAndResults. Accessed in January 2024.
27
- Available at: https://github.com/thomasWeise/jsspInstancesAndResults
28
-
29
- It includes the following information:
30
- - "optimum" (int | None): The optimal makespan for the instance.
31
- - "lower_bound" (int): The lower bound for the makespan. If
32
- optimality is known, it is equal to the optimum.
33
- - "upper_bound" (int): The upper bound for the makespan. If
34
- optimality is known, it is equal to the optimum.
35
- - "reference" (str): The paper or source where the instance was first
36
- introduced.
37
-
38
- References:
39
- - J. Adams, E. Balas, and D. Zawack, "The shifting bottleneck procedure
40
- for job shop scheduling," Management Science, vol. 34, no. 3,
41
- pp. 391–401, 1988.
42
-
43
- - J.F. Muth and G.L. Thompson, Industrial scheduling. Englewood Cliffs,
44
- NJ: Prentice-Hall, 1963.
45
-
46
- - S. Lawrence, "Resource constrained project scheduling: An experimental
47
- investigation of heuristic scheduling techniques (Supplement),"
48
- Carnegie-Mellon University, Graduate School of Industrial
49
- Administration, Pittsburgh, Pennsylvania, 1984.
50
-
51
- - D. Applegate and W. Cook, "A computational study of job-shop
52
- scheduling," ORSA Journal on Computer, vol. 3, no. 2, pp. 149–156,
53
- 1991.
54
-
55
- - R.H. Storer, S.D. Wu, and R. Vaccari, "New search spaces for
56
- sequencing problems with applications to job-shop scheduling,"
57
- Management Science, vol. 38, no. 10, pp. 1495–1509, 1992.
58
-
59
- - T. Yamada and R. Nakano, "A genetic algorithm applicable to
60
- large-scale job-shop problems," in Proceedings of the Second
61
- International Workshop on Parallel Problem Solving from Nature
62
- (PPSN'2), Brussels, Belgium, pp. 281–290, 1992.
63
-
64
- - E. Taillard, "Benchmarks for basic scheduling problems," European
65
- Journal of Operational Research, vol. 64, no. 2, pp. 278–285, 1993.
66
- """
67
-
68
- from typing import Any
69
-
70
- import functools
71
- import json
72
- from importlib import resources
73
-
74
- from job_shop_lib import JobShopInstance
75
-
76
-
77
- @functools.cache
78
- def load_all_benchmark_instances() -> dict[str, JobShopInstance]:
79
- """Loads all benchmark instances available.
80
-
81
- Returns:
82
- A dictionary containing the names of the benchmark instances as keys
83
- and the corresponding JobShopInstance objects as values.
84
-
85
- """
86
- benchmark_instances_dict = load_benchmark_json()
87
- return {
88
- name: load_benchmark_instance(name)
89
- for name in benchmark_instances_dict
90
- }
91
-
92
-
93
- def load_benchmark_instance(name: str) -> JobShopInstance:
94
- """Loads a specific benchmark instance.
95
-
96
- Calls to `load_benchmark_json` to load the benchmark instances from the
97
- JSON file. The instance is then loaded from the dictionary using the
98
- provided name. Since `load_benchmark_json` is cached, the file is only
99
- read once.
100
-
101
- Args:
102
- name: The name of the benchmark instance to load. Can be one of the
103
- following: "abz5-9", "ft06", "ft10", "ft20", "la01-40", "orb01-10",
104
- "swb01-20", "yn1-4", or "ta01-80".
105
- """
106
- benchmark_dict = load_benchmark_json()[name]
107
- return JobShopInstance.from_matrices(
108
- duration_matrix=benchmark_dict["duration_matrix"],
109
- machines_matrix=benchmark_dict["machines_matrix"],
110
- name=name,
111
- metadata=benchmark_dict["metadata"],
112
- )
113
-
114
-
115
- @functools.cache
116
- def load_benchmark_json() -> dict[str, dict[str, Any]]:
117
- """Loads the raw JSON file containing the benchmark instances.
118
-
119
- Results are cached to avoid reading the file multiple times.
120
-
121
- Each instance is represented as a dictionary with the following keys
122
- and values:
123
- - "name" (str): The name of the instance.
124
- - "duration_matrix" (list[list[int]]): The matrix containing the
125
- durations for each operation.
126
- - "machines_matrix" (list[list[int]]): The matrix containing the
127
- machines for each operation.
128
- - "metadata" (dict[str, Any]): A dictionary containing metadata
129
- about the instance. The keys are "optimum" (int | None),
130
- "lower_bound" (int), "upper_bound" (int),
131
- and "reference" (str).
132
-
133
- Returns:
134
- The dictionary containing the benchmark instances.
135
- """
136
- benchmark_file = (
137
- resources.files("job_shop_lib.benchmarking")
138
- / "benchmark_instances.json"
139
- )
140
-
141
- with benchmark_file.open("r", encoding="utf-8") as f:
142
- return json.load(f)
@@ -1,5 +0,0 @@
1
- from job_shop_lib.cp_sat.ortools_solver import ORToolsSolver
2
-
3
- __all__ = [
4
- "ORToolsSolver",
5
- ]
@@ -1,119 +0,0 @@
1
- """Home of the `DispatchingRuleSolver` class."""
2
-
3
- from collections.abc import Callable
4
-
5
- from job_shop_lib import JobShopInstance, Schedule, Operation, BaseSolver
6
- from job_shop_lib.dispatching import (
7
- dispatching_rule_factory,
8
- machine_chooser_factory,
9
- pruning_function_factory,
10
- DispatchingRule,
11
- MachineChooser,
12
- Dispatcher,
13
- PruningFunction,
14
- )
15
-
16
-
17
- class DispatchingRuleSolver(BaseSolver):
18
- """Solves a job shop instance using a dispatching rule.
19
-
20
- Attributes:
21
- dispatching_rule:
22
- The dispatching rule to use. It is a callable that takes a
23
- dispatcher and returns the operation to be dispatched next.
24
- machine_chooser:
25
- Used to choose the machine where the operation will be dispatched
26
- to. It is only used if the operation can be dispatched to multiple
27
- machines.
28
- pruning_function:
29
- The pruning function to use. It is used to initialize the
30
- dispatcher object internally when calling the solve method.
31
- """
32
-
33
- def __init__(
34
- self,
35
- dispatching_rule: (
36
- str | Callable[[Dispatcher], Operation]
37
- ) = DispatchingRule.MOST_WORK_REMAINING,
38
- machine_chooser: (
39
- str | Callable[[Dispatcher, Operation], int]
40
- ) = MachineChooser.FIRST,
41
- pruning_function: (
42
- str
43
- | Callable[[Dispatcher, list[Operation]], list[Operation]]
44
- | None
45
- ) = PruningFunction.DOMINATED_OPERATIONS,
46
- ):
47
- """Initializes the solver with the given dispatching rule, machine
48
- chooser and pruning function.
49
-
50
- Args:
51
- dispatching_rule:
52
- The dispatching rule to use. It can be a string with the name
53
- of the dispatching rule, a DispatchingRule enum member, or a
54
- callable that takes a dispatcher and returns the operation to
55
- be dispatched next.
56
- machine_chooser:
57
- The machine chooser to use. It can be a string with the name
58
- of the machine chooser, a MachineChooser enum member, or a
59
- callable that takes a dispatcher and an operation and returns
60
- the machine id where the operation will be dispatched.
61
- pruning_function:
62
- The pruning function to use. It can be a string with the name
63
- of the pruning function, a PruningFunction enum member, or a
64
- callable that takes a dispatcher and a list of operations and
65
- returns a list of operations that should be considered for
66
- dispatching.
67
- """
68
- if isinstance(dispatching_rule, str):
69
- dispatching_rule = dispatching_rule_factory(dispatching_rule)
70
- if isinstance(machine_chooser, str):
71
- machine_chooser = machine_chooser_factory(machine_chooser)
72
- if isinstance(pruning_function, str):
73
- pruning_function = pruning_function_factory(pruning_function)
74
-
75
- self.dispatching_rule = dispatching_rule
76
- self.machine_chooser = machine_chooser
77
- self.pruning_function = pruning_function
78
-
79
- def solve(
80
- self, instance: JobShopInstance, dispatcher: Dispatcher | None = None
81
- ) -> Schedule:
82
- """Returns a schedule for the given job shop instance using the
83
- dispatching rule algorithm."""
84
- if dispatcher is None:
85
- dispatcher = Dispatcher(
86
- instance, pruning_function=self.pruning_function
87
- )
88
- while not dispatcher.schedule.is_complete():
89
- self.step(dispatcher)
90
-
91
- return dispatcher.schedule
92
-
93
- def step(self, dispatcher: Dispatcher) -> None:
94
- """Executes one step of the dispatching rule algorithm.
95
-
96
- Args:
97
- dispatcher:
98
- The dispatcher object that will be used to dispatch the
99
- operations.
100
- """
101
- selected_operation = self.dispatching_rule(dispatcher)
102
- machine_id = self.machine_chooser(dispatcher, selected_operation)
103
- dispatcher.dispatch(selected_operation, machine_id)
104
-
105
-
106
- if __name__ == "__main__":
107
- import time
108
- from job_shop_lib.benchmarking import load_benchmark_instance
109
-
110
- ta_instances = []
111
- for i in range(1, 81):
112
- ta_instances.append(load_benchmark_instance(f"ta{i:02d}"))
113
- solver = DispatchingRuleSolver(dispatching_rule="most_work_remaining")
114
- # cProfile.run("for instance in ta_instances: solver.solve(instance)")
115
- start = time.perf_counter()
116
- for instance_ in ta_instances:
117
- solver.solve(instance_)
118
- end = time.perf_counter()
119
- print(f"Elapsed time: {end - start:.2f} seconds.")
@@ -1,206 +0,0 @@
1
- """Contains factory functions for creating dispatching rules, machine choosers,
2
- and pruning functions for the job shop scheduling problem.
3
-
4
- The factory functions create and return the appropriate functions based on the
5
- specified names or enums.
6
- """
7
-
8
- from enum import Enum
9
-
10
- from collections.abc import Callable, Sequence
11
- import random
12
-
13
- from job_shop_lib import Operation
14
- from job_shop_lib.dispatching import (
15
- shortest_processing_time_rule,
16
- first_come_first_served_rule,
17
- most_work_remaining_rule,
18
- most_operations_remaining_rule,
19
- random_operation_rule,
20
- Dispatcher,
21
- prune_dominated_operations,
22
- prune_non_immediate_machines,
23
- create_composite_pruning_function,
24
- )
25
-
26
-
27
- class DispatchingRule(str, Enum):
28
- """Enumeration of dispatching rules for the job shop scheduling problem."""
29
-
30
- SHORTEST_PROCESSING_TIME = "shortest_processing_time"
31
- FIRST_COME_FIRST_SERVED = "first_come_first_served"
32
- MOST_WORK_REMAINING = "most_work_remaining"
33
- MOST_OPERATIONS_REMAINING = "most_operations_remaining"
34
- RANDOM = "random"
35
-
36
-
37
- class MachineChooser(str, Enum):
38
- """Enumeration of machine chooser strategies for the job shop scheduling"""
39
-
40
- FIRST = "first"
41
- RANDOM = "random"
42
-
43
-
44
- class PruningFunction(str, Enum):
45
- """Enumeration of pruning functions.
46
-
47
- A pruning function is used by the `Dispatcher` class to reduce the
48
- amount of available operations to choose from.
49
- """
50
-
51
- DOMINATED_OPERATIONS = "dominated_operations"
52
- NON_IMMEDIATE_MACHINES = "non_immediate_machines"
53
-
54
-
55
- def dispatching_rule_factory(
56
- dispatching_rule: str | DispatchingRule,
57
- ) -> Callable[[Dispatcher], Operation]:
58
- """Creates and returns a dispatching rule function based on the specified
59
- dispatching rule name.
60
-
61
- The dispatching rule function determines the order in which operations are
62
- selected for execution based on certain criteria such as shortest
63
- processing time, first come first served, etc.
64
-
65
- Args:
66
- dispatching_rule: The name of the dispatching rule to be used.
67
- Supported values are 'shortest_processing_time',
68
- 'first_come_first_served', 'most_work_remaining',
69
- and 'random'.
70
-
71
- Returns:
72
- A function that takes a Dispatcher instance as input and returns an
73
- Operation based on the specified dispatching rule.
74
-
75
- Raises:
76
- ValueError: If the dispatching_rule argument is not recognized or is
77
- not supported.
78
- """
79
- dispatching_rules = {
80
- DispatchingRule.SHORTEST_PROCESSING_TIME: (
81
- shortest_processing_time_rule
82
- ),
83
- DispatchingRule.FIRST_COME_FIRST_SERVED: first_come_first_served_rule,
84
- DispatchingRule.MOST_WORK_REMAINING: most_work_remaining_rule,
85
- DispatchingRule.MOST_OPERATIONS_REMAINING: (
86
- most_operations_remaining_rule
87
- ),
88
- DispatchingRule.RANDOM: random_operation_rule,
89
- }
90
-
91
- dispatching_rule = dispatching_rule.lower()
92
- if dispatching_rule not in dispatching_rules:
93
- raise ValueError(
94
- f"Dispatching rule {dispatching_rule} not recognized. Available "
95
- f"dispatching rules: {', '.join(dispatching_rules)}."
96
- )
97
-
98
- return dispatching_rules[dispatching_rule] # type: ignore[index]
99
-
100
-
101
- def machine_chooser_factory(
102
- machine_chooser: str,
103
- ) -> Callable[[Dispatcher, Operation], int]:
104
- """Creates and returns a machine chooser function based on the specified
105
- machine chooser strategy name.
106
-
107
- The machine chooser function determines which machine an operation should
108
- be assigned to for execution. The selection can be based on different
109
- strategies such as choosing the first available machine or selecting a
110
- machine randomly.
111
-
112
- Args:
113
- machine_chooser (str): The name of the machine chooser strategy to be
114
- used. Supported values are 'first' and 'random'.
115
-
116
- Returns:
117
- A function that takes a Dispatcher instance and an Operation as input
118
- and returns the index of the selected machine based on the specified
119
- machine chooser strategy.
120
-
121
- Raises:
122
- ValueError: If the machine_chooser argument is not recognized or is
123
- not supported.
124
- """
125
- machine_choosers: dict[str, Callable[[Dispatcher, Operation], int]] = {
126
- MachineChooser.FIRST: lambda _, operation: operation.machines[0],
127
- MachineChooser.RANDOM: lambda _, operation: random.choice(
128
- operation.machines
129
- ),
130
- }
131
-
132
- machine_chooser = machine_chooser.lower()
133
- if machine_chooser not in machine_choosers:
134
- raise ValueError(
135
- f"Machine chooser {machine_chooser} not recognized. Available "
136
- f"machine choosers: {', '.join(machine_choosers)}."
137
- )
138
-
139
- return machine_choosers[machine_chooser]
140
-
141
-
142
- def composite_pruning_function_factory(
143
- pruning_function_names: Sequence[str | PruningFunction],
144
- ) -> Callable[[Dispatcher, list[Operation]], list[Operation]]:
145
- """Creates and returns a composite pruning function based on the
146
- specified list of pruning strategies.
147
-
148
- The composite pruning function filters out operations based on
149
- the specified list of pruning strategies.
150
-
151
- Args:
152
- pruning_functions:
153
- A list of pruning strategies to be used. Supported values are
154
- 'dominated_operations' and 'non_immediate_machines'.
155
-
156
- Returns:
157
- A function that takes a Dispatcher instance and a list of Operation
158
- instances as input and returns a list of Operation instances based on
159
- the specified list of pruning strategies.
160
-
161
- Raises:
162
- ValueError: If any of the pruning strategies in the list are not
163
- recognized or are not supported.
164
- """
165
-
166
- pruning_functions = [
167
- pruning_function_factory(name) for name in pruning_function_names
168
- ]
169
- return create_composite_pruning_function(pruning_functions)
170
-
171
-
172
- def pruning_function_factory(
173
- pruning_function_name: str | PruningFunction,
174
- ) -> Callable[[Dispatcher, list[Operation]], list[Operation]]:
175
- """Creates and returns a pruning function based on the specified
176
- pruning strategy name.
177
-
178
- The pruning function filters out operations based on certain
179
- criteria such as dominated operations, non-immediate machines, etc.
180
-
181
- Args:
182
- pruning_function:
183
- The name of the pruning function to be used. Supported values are
184
- 'dominated_operations' and 'non_immediate_machines'.
185
-
186
- Returns:
187
- A function that takes a Dispatcher instance and a list of Operation
188
- instances as input and returns a list of Operation instances based on
189
- the specified pruning function.
190
-
191
- Raises:
192
- ValueError: If the pruning_function argument is not recognized or is
193
- not supported.
194
- """
195
- pruning_strategies = {
196
- PruningFunction.DOMINATED_OPERATIONS: prune_dominated_operations,
197
- PruningFunction.NON_IMMEDIATE_MACHINES: prune_non_immediate_machines,
198
- }
199
-
200
- if pruning_function_name not in pruning_strategies:
201
- raise ValueError(
202
- f"Unsupported pruning function '{pruning_function_name}'. "
203
- f"Supported values are {', '.join(pruning_strategies.keys())}."
204
- )
205
-
206
- return pruning_strategies[pruning_function_name] # type: ignore[index]
@@ -1,87 +0,0 @@
1
- """Home of the `CompositeFeatureObserver` class."""
2
-
3
- from collections import defaultdict
4
- import numpy as np
5
- import pandas as pd
6
-
7
- from job_shop_lib.dispatching import Dispatcher
8
- from job_shop_lib.dispatching.feature_observers import (
9
- FeatureObserver,
10
- FeatureType,
11
- )
12
-
13
-
14
- class CompositeFeatureObserver(FeatureObserver):
15
- """Aggregates features from other FeatureObserver instances subscribed to
16
- the same `Dispatcher` by concatenating their feature matrices along the
17
- first axis (horizontal concatenation).
18
-
19
- Attributes:
20
- feature_observers:
21
- List of `FeatureObserver` instances to aggregate features from.
22
- column_names:
23
- Dictionary mapping `FeatureType` to a list of column names for the
24
- corresponding feature matrix. Column names are generated based on
25
- the class name of the `FeatureObserver` instance that produced the
26
- feature.
27
- """
28
-
29
- def __init__(
30
- self,
31
- dispatcher: Dispatcher,
32
- feature_observers: list[FeatureObserver] | None = None,
33
- subscribe: bool = True,
34
- ):
35
- if feature_observers is None:
36
- feature_observers = [
37
- observer
38
- for observer in dispatcher.subscribers
39
- if isinstance(observer, FeatureObserver)
40
- ]
41
- self.feature_observers = feature_observers
42
- self.column_names: dict[FeatureType, list[str]] = defaultdict(list)
43
- super().__init__(dispatcher, subscribe=subscribe)
44
- self._set_column_names()
45
-
46
- @property
47
- def features_as_dataframe(self) -> dict[FeatureType, pd.DataFrame]:
48
- """Returns the features as a dictionary of `pd.DataFrame` instances."""
49
- return {
50
- feature_type: pd.DataFrame(
51
- feature_matrix, columns=self.column_names[feature_type]
52
- )
53
- for feature_type, feature_matrix in self.features.items()
54
- }
55
-
56
- def initialize_features(self):
57
- features: dict[FeatureType, list[np.ndarray]] = defaultdict(list)
58
- for observer in self.feature_observers:
59
- for feature_type, feature_matrix in observer.features.items():
60
- features[feature_type].append(feature_matrix)
61
-
62
- self.features = {
63
- feature_type: np.concatenate(features, axis=1)
64
- for feature_type, features in features.items()
65
- }
66
-
67
- def _set_column_names(self):
68
- for observer in self.feature_observers:
69
- for feature_type, feature_matrix in observer.features.items():
70
- feature_name = observer.__class__.__name__.replace(
71
- "Observer", ""
72
- )
73
- if feature_matrix.shape[1] > 1:
74
- self.column_names[feature_type] += [
75
- f"{feature_name}_{i}"
76
- for i in range(feature_matrix.shape[1])
77
- ]
78
- else:
79
- self.column_names[feature_type].append(feature_name)
80
-
81
- def __str__(self):
82
- out = [f"{self.__class__.__name__}:"]
83
- out.append("-" * (len(out[0]) - 1))
84
- for feature_type, dataframe in self.features_as_dataframe.items():
85
- out.append(f"{feature_type.value}:")
86
- out.append(dataframe.to_string())
87
- return "\n".join(out)