job-shop-lib 1.1.1__tar.gz → 1.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/PKG-INFO +90 -19
  2. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/README.md +87 -17
  3. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/__init__.py +1 -1
  4. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/_job_shop_instance.py +5 -5
  5. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/_schedule.py +2 -2
  6. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_dispatcher.py +12 -7
  7. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_optimal_operations_observer.py +6 -3
  8. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +11 -46
  9. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +2 -2
  10. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_factory.py +2 -4
  11. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +3 -0
  12. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +0 -46
  13. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +106 -11
  14. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/generation/_general_instance_generator.py +6 -4
  15. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/_build_resource_task_graphs.py +1 -0
  16. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/_job_shop_graph.py +2 -2
  17. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +4 -3
  18. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py +5 -3
  19. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +23 -29
  20. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py +6 -4
  21. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py +18 -11
  22. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +1 -1
  23. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/pyproject.toml +8 -3
  24. job_shop_lib-1.1.1/job_shop_lib/generation/_transformations.py +0 -167
  25. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/LICENSE +0 -0
  26. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/_base_solver.py +0 -0
  27. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/_operation.py +0 -0
  28. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/_scheduled_operation.py +0 -0
  29. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/benchmarking/__init__.py +0 -0
  30. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/benchmarking/_load_benchmark.py +0 -0
  31. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
  32. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/constraint_programming/__init__.py +0 -0
  33. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/constraint_programming/_ortools_solver.py +0 -0
  34. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/__init__.py +0 -0
  35. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_dispatcher_observer_config.py +0 -0
  36. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_factories.py +0 -0
  37. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_history_observer.py +0 -0
  38. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_ready_operation_filters.py +0 -0
  39. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_start_time_calculators.py +0 -0
  40. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +0 -0
  41. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
  42. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +0 -0
  43. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +0 -0
  44. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +0 -0
  45. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
  46. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py +0 -0
  47. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
  48. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/rules/__init__.py +0 -0
  49. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +0 -0
  50. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +0 -0
  51. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/dispatching/rules/_utils.py +0 -0
  52. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/exceptions.py +0 -0
  53. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/generation/__init__.py +0 -0
  54. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/generation/_instance_generator.py +0 -0
  55. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/generation/_utils.py +0 -0
  56. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/__init__.py +0 -0
  57. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/_build_disjunctive_graph.py +0 -0
  58. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/_constants.py +0 -0
  59. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/_node.py +0 -0
  60. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/graph_updaters/__init__.py +0 -0
  61. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py +0 -0
  62. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +0 -0
  63. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +0 -0
  64. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/graphs/graph_updaters/_utils.py +0 -0
  65. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/py.typed +0 -0
  66. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/reinforcement_learning/__init__.py +0 -0
  67. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/reinforcement_learning/_reward_observers.py +0 -0
  68. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/reinforcement_learning/_types_and_constants.py +0 -0
  69. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/reinforcement_learning/_utils.py +0 -0
  70. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/__init__.py +0 -0
  71. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/gantt/__init__.py +0 -0
  72. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/gantt/_gantt_chart_creator.py +0 -0
  73. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/gantt/_plot_gantt_chart.py +0 -0
  74. {job_shop_lib-1.1.1 → job_shop_lib-1.1.3}/job_shop_lib/visualization/graphs/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 1.1.1
3
+ Version: 1.1.3
4
4
  Summary: An easy-to-use and modular Python library for the Job Shop Scheduling Problem (JSSP)
5
5
  License: MIT
6
6
  Author: Pabloo22
@@ -17,7 +17,8 @@ Requires-Dist: imageio[ffmpeg] (>=2.34.1,<3.0.0)
17
17
  Requires-Dist: matplotlib (>=3,<4)
18
18
  Requires-Dist: networkx (>=3,<4)
19
19
  Requires-Dist: numpy (>=1.26.4,<3.0.0)
20
- Requires-Dist: ortools (>=9.9,<10.0)
20
+ Requires-Dist: ortools (>=9.9,<10.0) ; sys_platform != "darwin"
21
+ Requires-Dist: ortools (>=9.9,<9.13) ; sys_platform == "darwin"
21
22
  Requires-Dist: pyarrow (>=15,<21)
22
23
  Requires-Dist: pygraphviz (>=1.12,<2.0) ; extra == "pygraphviz"
23
24
  Description-Content-Type: text/markdown
@@ -30,6 +31,7 @@ Description-Content-Type: text/markdown
30
31
 
31
32
  [![Tests](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml/badge.svg)](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml)
32
33
  [![Documentation Status](https://readthedocs.org/projects/job-shop-lib/badge/?version=latest)](https://job-shop-lib.readthedocs.io/en/latest/?badge=latest)
34
+ [![codecov](https://codecov.io/gh/Pabloo22/job_shop_lib/graph/badge.svg?token=DWXLYJWAOZ)](https://codecov.io/gh/Pabloo22/job_shop_lib)
33
35
  ![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)
34
36
  [![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
35
37
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -84,6 +86,24 @@ pip install job-shop-lib
84
86
 
85
87
  <!-- end key features -->
86
88
 
89
+ ## Publication :scroll:
90
+
91
+ For an in-depth explanation of the library (v1.0.0), including its design, features, reinforcement learning environments, and some experiments, please refer to my [Bachelor's thesis](https://www.arxiv.org/abs/2506.13781).
92
+
93
+ You can also cite the library using the following BibTeX entry:
94
+
95
+ ```bibtex
96
+ @misc{arino2025jobshoplib,
97
+ title={Solving the Job Shop Scheduling Problem with Graph Neural Networks: A Customizable Reinforcement Learning Environment},
98
+ author={Pablo Ariño Fernández},
99
+ year={2025},
100
+ eprint={2506.13781},
101
+ archivePrefix={arXiv},
102
+ primaryClass={cs.LG},
103
+ url={https://arxiv.org/abs/2506.13781},
104
+ }
105
+ ```
106
+
87
107
  ## Some Examples :rocket:
88
108
 
89
109
  ### Create a Job Shop Instance
@@ -328,31 +348,82 @@ plt.show()
328
348
 
329
349
  The library generalizes this graph by allowing the addition of job nodes and a global one (see `build_resource_task_graph_with_jobs` and `build_resource_task_graph`).
330
350
 
331
- For more details, check the [examples](examples) folder.
351
+ ### Gymnasium Environments
332
352
 
333
- ## Installation for development
353
+ <div align="center">
354
+ <img src="docs/source/images/rl_diagram.png">
355
+ </div>
356
+ <br>
334
357
 
335
- <!-- start installation development -->
336
358
 
337
- 1. Clone the repository.
359
+ The `SingleJobShopGraphEnv` allows to learn from a single job shop instance, while the `MultiJobShopGraphEnv` generates a new instance at each reset. For an in-depth explanation of the environments see chapter 7 of my [Bachelor's thesis](https://www.arxiv.org/abs/2506.13781).
338
360
 
339
- ```bash
340
- git clone https://github.com/Pabloo22/job_shop_lib.git
341
- cd job_shop_lib
342
- ```
361
+ ```python
362
+ from IPython.display import clear_output
363
+
364
+ from job_shop_lib.reinforcement_learning import (
365
+ # MakespanReward,
366
+ SingleJobShopGraphEnv,
367
+ ObservationSpaceKey,
368
+ IdleTimeReward,
369
+ ObservationDict,
370
+ )
371
+ from job_shop_lib.dispatching.feature_observers import (
372
+ FeatureObserverType,
373
+ FeatureType,
374
+ )
375
+ from job_shop_lib.dispatching import DispatcherObserverConfig
376
+
377
+
378
+ instance = load_benchmark_instance("ft06")
379
+ job_shop_graph = build_disjunctive_graph(instance)
380
+ feature_observer_configs = [
381
+ DispatcherObserverConfig(
382
+ FeatureObserverType.IS_READY,
383
+ kwargs={"feature_types": [FeatureType.JOBS]},
384
+ )
385
+ ]
386
+
387
+ env = SingleJobShopGraphEnv(
388
+ job_shop_graph=job_shop_graph,
389
+ feature_observer_configs=feature_observer_configs,
390
+ reward_function_config=DispatcherObserverConfig(IdleTimeReward),
391
+ render_mode="human", # Try "save_video"
392
+ render_config={
393
+ "video_config": {"fps": 4}
394
+ }
395
+ )
343
396
 
344
- 2. Install [poetry](https://python-poetry.org/docs/) if you don't have it already:
345
397
 
346
- ```bash
347
- pip install poetry
348
- ```
398
+ def random_action(observation: ObservationDict) -> tuple[int, int]:
399
+ ready_jobs = []
400
+ for job_id, is_ready in enumerate(
401
+ observation[ObservationSpaceKey.JOBS.value].ravel()
402
+ ):
403
+ if is_ready == 1.0:
404
+ ready_jobs.append(job_id)
349
405
 
350
- 3. Install dependencies:
351
- ```bash
352
- make poetry_install_all
406
+ job_id = random.choice(ready_jobs)
407
+ machine_id = -1 # We can use -1 if each operation can only be scheduled
408
+ # on one machine.
409
+ return (job_id, machine_id)
410
+
411
+
412
+ done = False
413
+ obs, _ = env.reset()
414
+ while not done:
415
+ action = random_action(obs)
416
+ obs, reward, done, *_ = env.step(action)
417
+ if env.render_mode == "human":
418
+ env.render()
419
+ clear_output(wait=True)
420
+
421
+ if env.render_mode == "save_video" or env.render_mode == "save_gif":
422
+ env.render()
353
423
  ```
354
424
 
355
- <!-- end installation development -->
425
+ ## Contributing :handshake:
426
+ Any contribution is welcome, whether it's a small bug or documentation fix or a new feature! See the [CONTRIBUTING.md](CONTRIBUTING.md) file for details on how to contribute to this project.
356
427
 
357
428
  ## License :scroll:
358
429
 
@@ -389,5 +460,5 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
389
460
  - E. Taillard, "Benchmarks for basic scheduling problems," European
390
461
  Journal of Operational Research, vol. 64, no. 2, pp. 278–285, 1993.
391
462
 
392
- - Park, Junyoung, Sanjar Bakhtiyar, and Jinkyoo Park. "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning." arXiv preprint arXiv:2106.03051, 2021.
463
+ - Park, Junyoung, Sanjar Bakhtiyar, and Jinkyoo Park. "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning." arXiv preprint arXiv:2106.03051, 2021.
393
464
 
@@ -6,6 +6,7 @@
6
6
 
7
7
  [![Tests](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml/badge.svg)](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml)
8
8
  [![Documentation Status](https://readthedocs.org/projects/job-shop-lib/badge/?version=latest)](https://job-shop-lib.readthedocs.io/en/latest/?badge=latest)
9
+ [![codecov](https://codecov.io/gh/Pabloo22/job_shop_lib/graph/badge.svg?token=DWXLYJWAOZ)](https://codecov.io/gh/Pabloo22/job_shop_lib)
9
10
  ![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)
10
11
  [![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
11
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -60,6 +61,24 @@ pip install job-shop-lib
60
61
 
61
62
  <!-- end key features -->
62
63
 
64
+ ## Publication :scroll:
65
+
66
+ For an in-depth explanation of the library (v1.0.0), including its design, features, reinforcement learning environments, and some experiments, please refer to my [Bachelor's thesis](https://www.arxiv.org/abs/2506.13781).
67
+
68
+ You can also cite the library using the following BibTeX entry:
69
+
70
+ ```bibtex
71
+ @misc{arino2025jobshoplib,
72
+ title={Solving the Job Shop Scheduling Problem with Graph Neural Networks: A Customizable Reinforcement Learning Environment},
73
+ author={Pablo Ariño Fernández},
74
+ year={2025},
75
+ eprint={2506.13781},
76
+ archivePrefix={arXiv},
77
+ primaryClass={cs.LG},
78
+ url={https://arxiv.org/abs/2506.13781},
79
+ }
80
+ ```
81
+
63
82
  ## Some Examples :rocket:
64
83
 
65
84
  ### Create a Job Shop Instance
@@ -304,31 +323,82 @@ plt.show()
304
323
 
305
324
  The library generalizes this graph by allowing the addition of job nodes and a global one (see `build_resource_task_graph_with_jobs` and `build_resource_task_graph`).
306
325
 
307
- For more details, check the [examples](examples) folder.
326
+ ### Gymnasium Environments
308
327
 
309
- ## Installation for development
328
+ <div align="center">
329
+ <img src="docs/source/images/rl_diagram.png">
330
+ </div>
331
+ <br>
310
332
 
311
- <!-- start installation development -->
312
333
 
313
- 1. Clone the repository.
334
+ The `SingleJobShopGraphEnv` allows to learn from a single job shop instance, while the `MultiJobShopGraphEnv` generates a new instance at each reset. For an in-depth explanation of the environments see chapter 7 of my [Bachelor's thesis](https://www.arxiv.org/abs/2506.13781).
314
335
 
315
- ```bash
316
- git clone https://github.com/Pabloo22/job_shop_lib.git
317
- cd job_shop_lib
318
- ```
336
+ ```python
337
+ from IPython.display import clear_output
338
+
339
+ from job_shop_lib.reinforcement_learning import (
340
+ # MakespanReward,
341
+ SingleJobShopGraphEnv,
342
+ ObservationSpaceKey,
343
+ IdleTimeReward,
344
+ ObservationDict,
345
+ )
346
+ from job_shop_lib.dispatching.feature_observers import (
347
+ FeatureObserverType,
348
+ FeatureType,
349
+ )
350
+ from job_shop_lib.dispatching import DispatcherObserverConfig
351
+
352
+
353
+ instance = load_benchmark_instance("ft06")
354
+ job_shop_graph = build_disjunctive_graph(instance)
355
+ feature_observer_configs = [
356
+ DispatcherObserverConfig(
357
+ FeatureObserverType.IS_READY,
358
+ kwargs={"feature_types": [FeatureType.JOBS]},
359
+ )
360
+ ]
361
+
362
+ env = SingleJobShopGraphEnv(
363
+ job_shop_graph=job_shop_graph,
364
+ feature_observer_configs=feature_observer_configs,
365
+ reward_function_config=DispatcherObserverConfig(IdleTimeReward),
366
+ render_mode="human", # Try "save_video"
367
+ render_config={
368
+ "video_config": {"fps": 4}
369
+ }
370
+ )
319
371
 
320
- 2. Install [poetry](https://python-poetry.org/docs/) if you don't have it already:
321
372
 
322
- ```bash
323
- pip install poetry
324
- ```
373
+ def random_action(observation: ObservationDict) -> tuple[int, int]:
374
+ ready_jobs = []
375
+ for job_id, is_ready in enumerate(
376
+ observation[ObservationSpaceKey.JOBS.value].ravel()
377
+ ):
378
+ if is_ready == 1.0:
379
+ ready_jobs.append(job_id)
325
380
 
326
- 3. Install dependencies:
327
- ```bash
328
- make poetry_install_all
381
+ job_id = random.choice(ready_jobs)
382
+ machine_id = -1 # We can use -1 if each operation can only be scheduled
383
+ # on one machine.
384
+ return (job_id, machine_id)
385
+
386
+
387
+ done = False
388
+ obs, _ = env.reset()
389
+ while not done:
390
+ action = random_action(obs)
391
+ obs, reward, done, *_ = env.step(action)
392
+ if env.render_mode == "human":
393
+ env.render()
394
+ clear_output(wait=True)
395
+
396
+ if env.render_mode == "save_video" or env.render_mode == "save_gif":
397
+ env.render()
329
398
  ```
330
399
 
331
- <!-- end installation development -->
400
+ ## Contributing :handshake:
401
+ Any contribution is welcome, whether it's a small bug or documentation fix or a new feature! See the [CONTRIBUTING.md](CONTRIBUTING.md) file for details on how to contribute to this project.
332
402
 
333
403
  ## License :scroll:
334
404
 
@@ -365,4 +435,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
365
435
  - E. Taillard, "Benchmarks for basic scheduling problems," European
366
436
  Journal of Operational Research, vol. 64, no. 2, pp. 278–285, 1993.
367
437
 
368
- - Park, Junyoung, Sanjar Bakhtiyar, and Jinkyoo Park. "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning." arXiv preprint arXiv:2106.03051, 2021.
438
+ - Park, Junyoung, Sanjar Bakhtiyar, and Jinkyoo Park. "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning." arXiv preprint arXiv:2106.03051, 2021.
@@ -19,7 +19,7 @@ from job_shop_lib._schedule import Schedule
19
19
  from job_shop_lib._base_solver import BaseSolver, Solver
20
20
 
21
21
 
22
- __version__ = "1.1.1"
22
+ __version__ = "1.1.3"
23
23
 
24
24
  __all__ = [
25
25
  "Operation",
@@ -13,7 +13,7 @@ from job_shop_lib import Operation
13
13
 
14
14
 
15
15
  class JobShopInstance:
16
- """Data structure to store a Job Shop Scheduling Problem instance.
16
+ r"""Data structure to store a Job Shop Scheduling Problem instance.
17
17
 
18
18
  Additional attributes such as ``num_machines`` or ``durations_matrix`` can
19
19
  be computed from the instance and are cached for performance since they
@@ -75,7 +75,7 @@ class JobShopInstance:
75
75
  attributes of the operations are set when the instance is created.
76
76
  See :meth:`set_operation_attributes` for more information. Defaults
77
77
  to True.
78
- **metadata:
78
+ \**metadata:
79
79
  Additional information about the instance.
80
80
  """
81
81
 
@@ -131,7 +131,7 @@ class JobShopInstance:
131
131
  name: str | None = None,
132
132
  **metadata: Any,
133
133
  ) -> JobShopInstance:
134
- """Creates a JobShopInstance from a file following Taillard's format.
134
+ r"""Creates a JobShopInstance from a file following Taillard's format.
135
135
 
136
136
  Args:
137
137
  file_path:
@@ -144,7 +144,7 @@ class JobShopInstance:
144
144
  name:
145
145
  A string with the name of the instance. If not provided, the
146
146
  name of the instance is set to the name of the file.
147
- **metadata:
147
+ \**metadata:
148
148
  Additional information about the instance.
149
149
 
150
150
  Returns:
@@ -221,7 +221,7 @@ class JobShopInstance:
221
221
  A list of lists of integers. The i-th list contains the
222
222
  durations of the operations of the job with id i.
223
223
  machines_matrix:
224
- A list of lists of lists of integers if the
224
+ A list of lists of lists of integers if the
225
225
  instance is flexible, or a list of lists of integers if the
226
226
  instance is not flexible. The i-th list contains the machines
227
227
  in which the operations of the job with id i can be processed.
@@ -10,7 +10,7 @@ from job_shop_lib.exceptions import ValidationError
10
10
 
11
11
 
12
12
  class Schedule:
13
- """Data structure to store a complete or partial solution for a particular
13
+ r"""Data structure to store a complete or partial solution for a particular
14
14
  :class:`JobShopInstance`.
15
15
 
16
16
  A schedule is a list of lists of :class:`ScheduledOperation` objects. Each
@@ -33,7 +33,7 @@ class Schedule:
33
33
  A list of lists of :class:`ScheduledOperation` objects. Each
34
34
  list represents the order of operations on a machine. If
35
35
  not provided, the schedule is initialized as an empty schedule.
36
- **metadata:
36
+ \**metadata:
37
37
  Additional information about the schedule.
38
38
  """
39
39
 
@@ -30,9 +30,12 @@ def no_setup_time_calculator(
30
30
  operation belongs.
31
31
 
32
32
  Args:
33
- dispatcher: The dispatcher instance.
34
- operation: The operation to be scheduled.
35
- machine_id: The id of the machine on which the operation is to be
33
+ dispatcher:
34
+ The dispatcher instance.
35
+ operation:
36
+ The operation to be scheduled.
37
+ machine_id:
38
+ The id of the machine on which the operation is to be
36
39
  scheduled.
37
40
 
38
41
  Returns:
@@ -329,8 +332,10 @@ class Dispatcher:
329
332
  :attr:`~job_shop_lib.Operation.machine_id` attribute is used.
330
333
 
331
334
  Raises:
332
- ValidationError: If the operation is not ready to be scheduled.
333
- UninitializedAttributeError: If the operation has multiple
335
+ ValidationError:
336
+ If the operation is not ready to be scheduled.
337
+ UninitializedAttributeError:
338
+ If the operation has multiple
334
339
  machines in its list and no ``machine_id`` is provided.
335
340
  """
336
341
 
@@ -408,7 +413,7 @@ class Dispatcher:
408
413
  condition: Callable[[DispatcherObserver], bool] = lambda _: True,
409
414
  **kwargs,
410
415
  ) -> ObserverType:
411
- """Creates a new observer of the specified type or returns an existing
416
+ r"""Creates a new observer of the specified type or returns an existing
412
417
  observer of the same type if it already exists in the dispatcher's list
413
418
  of observers.
414
419
 
@@ -419,7 +424,7 @@ class Dispatcher:
419
424
  A function that takes an observer and returns True if it is
420
425
  the observer to be retrieved. By default, it returns True for
421
426
  all observers.
422
- **kwargs:
427
+ \**kwargs:
423
428
  Additional keyword arguments to be passed to the observer's
424
429
  constructor.
425
430
  """
@@ -21,10 +21,13 @@ class OptimalOperationsObserver(DispatcherObserver):
21
21
  operations.
22
22
 
23
23
  Args:
24
- dispatcher: The dispatcher instance to observe.
25
- reference_schedule: A complete schedule that represents the optimal
24
+ dispatcher:
25
+ The dispatcher instance to observe.
26
+ reference_schedule:
27
+ A complete schedule that represents the optimal
26
28
  or reference solution.
27
- subscribe: If True, automatically subscribes to the dispatcher.
29
+ subscribe:
30
+ If True, automatically subscribes to the dispatcher.
28
31
 
29
32
  Raises:
30
33
  ValidationError: If the reference schedule is incomplete or if it
@@ -11,7 +11,6 @@ import numpy as np
11
11
  from numpy.typing import NDArray
12
12
  import pandas as pd
13
13
 
14
- from job_shop_lib.exceptions import ValidationError
15
14
  from job_shop_lib.dispatching import Dispatcher
16
15
  from job_shop_lib.dispatching.feature_observers import (
17
16
  FeatureObserver,
@@ -86,19 +85,11 @@ class CompositeFeatureObserver(FeatureObserver):
86
85
  for observer in dispatcher.subscribers
87
86
  if isinstance(observer, FeatureObserver)
88
87
  ]
89
- feature_types = self._get_feature_types_list(feature_types)
90
- for observer in feature_observers:
91
- if not set(observer.features.keys()).issubset(set(feature_types)):
92
- raise ValidationError(
93
- "The feature types observed by the feature observer "
94
- f"{observer.__class__.__name__} are not a subset of the "
95
- "feature types specified in the CompositeFeatureObserver."
96
- f"Observer feature types: {observer.features.keys()}"
97
- f"Composite feature types: {feature_types}"
98
- )
99
88
  self.feature_observers = feature_observers
100
89
  self.column_names: dict[FeatureType, list[str]] = defaultdict(list)
101
- super().__init__(dispatcher, subscribe=subscribe)
90
+ super().__init__(
91
+ dispatcher, subscribe=subscribe, feature_types=feature_types
92
+ )
102
93
  self._set_column_names()
103
94
 
104
95
  @classmethod
@@ -148,12 +139,17 @@ class CompositeFeatureObserver(FeatureObserver):
148
139
  list
149
140
  )
150
141
  for observer in self.feature_observers:
151
- for feature_type, feature_matrix in observer.features.items():
142
+ for feature_type in self.supported_feature_types:
143
+ feature_matrix = observer.features.get(feature_type)
144
+ if feature_matrix is None:
145
+ continue
152
146
  features[feature_type].append(feature_matrix)
153
147
 
154
148
  self.features = {
155
- feature_type: np.concatenate(features, axis=1)
156
- for feature_type, features in features.items()
149
+ feature_type: np.concatenate(
150
+ feature_matrices, axis=1 # type: ignore[misc]
151
+ )
152
+ for feature_type, feature_matrices in features.items()
157
153
  }
158
154
 
159
155
  def _set_column_names(self):
@@ -177,34 +173,3 @@ class CompositeFeatureObserver(FeatureObserver):
177
173
  out.append(f"{feature_type.value}:")
178
174
  out.append(dataframe.to_string())
179
175
  return "\n".join(out)
180
-
181
-
182
- if __name__ == "__main__":
183
- # from cProfile import Profile
184
- import time
185
- from job_shop_lib.benchmarking import load_benchmark_instance
186
- from job_shop_lib.dispatching.rules import DispatchingRuleSolver
187
-
188
- ta80 = load_benchmark_instance("ta80")
189
-
190
- dispatcher_ = Dispatcher(ta80)
191
- feature_observer_types_ = list(FeatureObserverType)
192
- feature_observers_ = [
193
- feature_observer_factory(
194
- observer_type,
195
- dispatcher=dispatcher_,
196
- )
197
- for observer_type in feature_observer_types_
198
- # and not FeatureObserverType.EARLIEST_START_TIME
199
- ]
200
- composite_observer_ = CompositeFeatureObserver(
201
- dispatcher_, feature_observers=feature_observers_
202
- )
203
- solver = DispatchingRuleSolver(dispatching_rule="random")
204
- # profiler = Profile()
205
- # profiler.runcall(solver.solve, dispatcher_.instance, dispatcher_)
206
- # profiler.print_stats("cumtime")
207
- start = time.perf_counter()
208
- solver.solve(dispatcher_.instance, dispatcher_)
209
- end = time.perf_counter()
210
- print(f"Time: {end - start}")
@@ -93,8 +93,8 @@ class EarliestStartTimeObserver(FeatureObserver):
93
93
  # Cache:
94
94
  operations_by_machine = dispatcher.instance.operations_by_machine
95
95
  self._is_regular_instance = all(
96
- len(job) == len(dispatcher.instance.jobs[0])
97
- for job in dispatcher.instance.jobs
96
+ len(machine_ops) == len(operations_by_machine[0])
97
+ for machine_ops in operations_by_machine
98
98
  )
99
99
  if self._is_regular_instance:
100
100
  self._job_ids = np.array(
@@ -1,5 +1,3 @@
1
- """Contains factory functions for creating :class:`FeatureObserver`s."""
2
-
3
1
  from enum import Enum
4
2
 
5
3
  from job_shop_lib.dispatching import DispatcherObserverConfig
@@ -57,13 +55,13 @@ def feature_observer_factory(
57
55
  ),
58
56
  **kwargs,
59
57
  ) -> FeatureObserver:
60
- """Creates and returns a :class:`FeatureObserver` based on the specified
58
+ r"""Creates and returns a :class:`FeatureObserver` based on the specified
61
59
  :class:`FeatureObserver` type.
62
60
 
63
61
  Args:
64
62
  feature_creator_type:
65
63
  The type of :class:`FeatureObserver` to create.
66
- **kwargs:
64
+ \*\*kwargs:
67
65
  Additional keyword arguments to pass to the
68
66
  :class:`FeatureObserver` constructor.
69
67
 
@@ -92,6 +92,9 @@ class FeatureObserver(DispatcherObserver):
92
92
  feature_type: self._feature_sizes
93
93
  for feature_type in feature_types
94
94
  }
95
+ else:
96
+ feature_size = self._feature_sizes
97
+
95
98
  super().__init__(dispatcher, subscribe=subscribe)
96
99
 
97
100
  number_of_entities = {
@@ -148,49 +148,3 @@ class DispatchingRuleSolver(BaseSolver):
148
148
  selected_operation = self.dispatching_rule(dispatcher)
149
149
  machine_id = self.machine_chooser(dispatcher, selected_operation)
150
150
  dispatcher.dispatch(selected_operation, machine_id)
151
-
152
-
153
- if __name__ == "__main__":
154
- import time
155
- import cProfile
156
-
157
- # import pstats
158
- # from io import StringIO
159
- from job_shop_lib.benchmarking import (
160
- # load_benchmark_instance,
161
- load_all_benchmark_instances,
162
- )
163
-
164
- # from job_shop_lib.dispatching.rules._dispatching_rules_functions import (
165
- # most_work_remaining_rule_2,
166
- # )
167
-
168
- # ta_instances = [
169
- # load_benchmark_instance(f"ta{i:02d}") for i in range(1, 81)
170
- # ]
171
- ta_instances = load_all_benchmark_instances().values()
172
- solver = DispatchingRuleSolver(
173
- dispatching_rule="most_work_remaining", ready_operations_filter=None
174
- )
175
-
176
- start = time.perf_counter()
177
-
178
- # Create a Profile object
179
- profiler = cProfile.Profile()
180
-
181
- # Run the code under profiling
182
- # profiler.enable()
183
- for instance_ in ta_instances:
184
- solver.solve(instance_)
185
- # profiler.disable()
186
-
187
- end = time.perf_counter()
188
-
189
- # Print elapsed time
190
- print(f"Elapsed time: {end - start:.2f} seconds.")
191
-
192
- # Print profiling results
193
- # s = StringIO()
194
- # ps = pstats.Stats(profiler, stream=s).sort_stats("cumulative")
195
- # profiler.print_stats("cumtime") # Print top 20 time-consuming functions
196
- # print(s.getvalue())