job-shop-lib 1.6.0__tar.gz → 1.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/PKG-INFO +4 -5
  2. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/README.md +2 -2
  3. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/__init__.py +1 -1
  4. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_job_shop_instance.py +37 -5
  5. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_schedule.py +13 -4
  6. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/benchmarking/__init__.py +3 -0
  7. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/benchmarking/_load_benchmark.py +25 -0
  8. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +1 -1
  9. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +1 -1
  10. job_shop_lib-1.7.0/job_shop_lib/generation/__init__.py +71 -0
  11. job_shop_lib-1.7.0/job_shop_lib/generation/_duration_matrix.py +83 -0
  12. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/generation/_instance_generator.py +8 -6
  13. job_shop_lib-1.6.0/job_shop_lib/generation/_utils.py → job_shop_lib-1.7.0/job_shop_lib/generation/_machine_matrix.py +50 -59
  14. job_shop_lib-1.7.0/job_shop_lib/generation/_modular_instance_generator.py +116 -0
  15. job_shop_lib-1.7.0/job_shop_lib/generation/_release_date_matrix.py +160 -0
  16. job_shop_lib-1.7.0/job_shop_lib/generation/_size_selectors.py +58 -0
  17. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/__init__.py +6 -0
  18. job_shop_lib-1.7.0/job_shop_lib/metaheuristics/_objective_functions.py +114 -0
  19. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/__init__.py +9 -0
  20. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_reward_observers.py +76 -0
  21. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_utils.py +63 -4
  22. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/pyproject.toml +2 -5
  23. job_shop_lib-1.6.0/job_shop_lib/generation/__init__.py +0 -30
  24. job_shop_lib-1.6.0/job_shop_lib/metaheuristics/_objective_functions.py +0 -73
  25. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/LICENSE +0 -0
  26. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_base_solver.py +0 -0
  27. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_operation.py +0 -0
  28. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_scheduled_operation.py +0 -0
  29. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
  30. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/constraint_programming/__init__.py +0 -0
  31. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/constraint_programming/_ortools_solver.py +0 -0
  32. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/__init__.py +0 -0
  33. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_dispatcher.py +0 -0
  34. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_dispatcher_observer_config.py +0 -0
  35. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_factories.py +0 -0
  36. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_history_observer.py +0 -0
  37. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_optimal_operations_observer.py +0 -0
  38. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_ready_operation_filters.py +0 -0
  39. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_start_time_calculators.py +0 -0
  40. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +0 -0
  41. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
  42. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +0 -0
  43. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_dates_observer.py +0 -0
  44. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_factory.py +0 -0
  45. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +0 -0
  46. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +0 -0
  47. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +0 -0
  48. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
  49. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py +0 -0
  50. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
  51. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/__init__.py +0 -0
  52. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +0 -0
  53. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +0 -0
  54. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +0 -0
  55. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +0 -0
  56. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_utils.py +0 -0
  57. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/exceptions.py +0 -0
  58. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/generation/_general_instance_generator.py +0 -0
  59. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/__init__.py +0 -0
  60. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_build_disjunctive_graph.py +0 -0
  61. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_build_resource_task_graphs.py +0 -0
  62. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_constants.py +0 -0
  63. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_job_shop_graph.py +0 -0
  64. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_node.py +0 -0
  65. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/__init__.py +0 -0
  66. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py +0 -0
  67. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +0 -0
  68. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +0 -0
  69. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_utils.py +0 -0
  70. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/_job_shop_annealer.py +0 -0
  71. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/_neighbor_generators.py +0 -0
  72. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/_simulated_annealing_solver.py +0 -0
  73. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/py.typed +0 -0
  74. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +0 -0
  75. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py +0 -0
  76. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +0 -0
  77. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_types_and_constants.py +0 -0
  78. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/__init__.py +0 -0
  79. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/gantt/__init__.py +0 -0
  80. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/gantt/_gantt_chart_creator.py +0 -0
  81. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py +0 -0
  82. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/gantt/_plot_gantt_chart.py +0 -0
  83. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/graphs/__init__.py +0 -0
  84. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py +0 -0
  85. {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 1.6.0
3
+ Version: 1.7.0
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,8 +17,7 @@ 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) ; sys_platform != "darwin"
21
- Requires-Dist: ortools (>=9.9,<9.13) ; sys_platform == "darwin"
20
+ Requires-Dist: ortools (>=9.9,<9.13)
22
21
  Requires-Dist: pyarrow (>=15,<21)
23
22
  Requires-Dist: pygraphviz (>=1.12,<2.0) ; extra == "pygraphviz"
24
23
  Requires-Dist: simanneal (>=0.5.0,<0.6.0)
@@ -46,13 +45,13 @@ It follows a modular design, allowing users to easily extend the library with ne
46
45
  We support multiple solvers, including:
47
46
  - **Constraint Programming**: Based on OR-Tools' CP-SAT solver. It supports **release dates, deadlines, and due dates.** See the ["Solving the Problem" tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/02-Solving-the-Problem.ipynb) for an example.
48
47
  - **Dispatching Rules**: A set of predefined rules and the ability to create custom ones. They support arbitrary **setup times, machine breakdowns, release dates, deadlines, and due dates**. See the [following example](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/03-Dispatching-Rules.ipynb). You can also create videos or GIFs of the scheduling process. For creating GIFs or videos, see the [Save Gif example](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/04-Save-Gif.ipynb).
49
- - **Metaheuristics**: Currently, we have a **simulated annealing** implementation that supports **release dates, deadlines, and due dates**. We also support arbitrary neighborhood search strategies, including swapping operations in the critical path as described in the paper "Job Shop Scheduling by Simulated Annealing" by van Laarhoven et al. (1992); and energy functions. See our [simulated annealing tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/03-Simulated-Annealing.ipynb).
48
+ - **Metaheuristics**: Currently, we have a **simulated annealing** implementation that supports **release dates, deadlines, and due dates**. We also support arbitrary neighborhood search strategies, including swapping operations in the critical path as described in the paper "Job Shop Scheduling by Simulated Annealing" by van Laarhoven et al. (1992); and energy functions. See our [simulated annealing tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/04-Simulated-Annealing.ipynb).
50
49
  - **Reinforcement Learning**: Two Gymnasium environments for solving the problem with **graph neural networks** (GNNs) or any other method. The environments support **setup times, release dates, deadlines, and due dates.** We're currently building a tutorial on how to use them.
51
50
 
52
51
  We also provide useful utilities, data structures, and visualization functions:
53
52
  - **Intuitive Data Structures**: Easily create, manage, and manipulate job shop instances and solutions with user-friendly data structures. See [Getting Started](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/00-Getting-Started.ipynb) and [How Solutions are Represented](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/01-How-Solutions-are-Represented.ipynb).
54
53
  - **Benchmark Instances**: Load well-known benchmark instances directly from the library without manual downloading. See [Load Benchmark Instances](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/05-Load-Benchmark-Instances.ipynb).
55
- - **Random Instance Generation**: Create random instances with customizable sizes and properties. See [`generation`](https://job-shop-lib.readthedocs.io/en/stable/api/job_shop_lib.generation.html#module-job_shop_lib.generation) module.
54
+ - **Random Instance Generation**: Create random instances with customizable sizes and properties. See [`this tutorial`](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/03-Generating-New-Instances.ipynb).
56
55
  - **Gantt Charts**: Visualize final schedules and how they are created iteratively by dispatching rule solvers or sequences of scheduling decisions with GIFs or videos.
57
56
  - **Graph Representations**: Represent and visualize instances as disjunctive graphs or agent-task graphs (introduced in the ScheduleNet paper). Build your own custom graphs with the `JobShopGraph` class. See the [Disjunctive Graphs](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/04-Disjunctive-Graphs.ipynb) and [Resource Task Graphs](https://job-shop-lib.readthedocs.io/en/stable/examples/07-Resource-Task-Graph.html) examples.
58
57
 
@@ -20,13 +20,13 @@ It follows a modular design, allowing users to easily extend the library with ne
20
20
  We support multiple solvers, including:
21
21
  - **Constraint Programming**: Based on OR-Tools' CP-SAT solver. It supports **release dates, deadlines, and due dates.** See the ["Solving the Problem" tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/02-Solving-the-Problem.ipynb) for an example.
22
22
  - **Dispatching Rules**: A set of predefined rules and the ability to create custom ones. They support arbitrary **setup times, machine breakdowns, release dates, deadlines, and due dates**. See the [following example](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/03-Dispatching-Rules.ipynb). You can also create videos or GIFs of the scheduling process. For creating GIFs or videos, see the [Save Gif example](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/04-Save-Gif.ipynb).
23
- - **Metaheuristics**: Currently, we have a **simulated annealing** implementation that supports **release dates, deadlines, and due dates**. We also support arbitrary neighborhood search strategies, including swapping operations in the critical path as described in the paper "Job Shop Scheduling by Simulated Annealing" by van Laarhoven et al. (1992); and energy functions. See our [simulated annealing tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/03-Simulated-Annealing.ipynb).
23
+ - **Metaheuristics**: Currently, we have a **simulated annealing** implementation that supports **release dates, deadlines, and due dates**. We also support arbitrary neighborhood search strategies, including swapping operations in the critical path as described in the paper "Job Shop Scheduling by Simulated Annealing" by van Laarhoven et al. (1992); and energy functions. See our [simulated annealing tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/04-Simulated-Annealing.ipynb).
24
24
  - **Reinforcement Learning**: Two Gymnasium environments for solving the problem with **graph neural networks** (GNNs) or any other method. The environments support **setup times, release dates, deadlines, and due dates.** We're currently building a tutorial on how to use them.
25
25
 
26
26
  We also provide useful utilities, data structures, and visualization functions:
27
27
  - **Intuitive Data Structures**: Easily create, manage, and manipulate job shop instances and solutions with user-friendly data structures. See [Getting Started](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/00-Getting-Started.ipynb) and [How Solutions are Represented](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/01-How-Solutions-are-Represented.ipynb).
28
28
  - **Benchmark Instances**: Load well-known benchmark instances directly from the library without manual downloading. See [Load Benchmark Instances](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/05-Load-Benchmark-Instances.ipynb).
29
- - **Random Instance Generation**: Create random instances with customizable sizes and properties. See [`generation`](https://job-shop-lib.readthedocs.io/en/stable/api/job_shop_lib.generation.html#module-job_shop_lib.generation) module.
29
+ - **Random Instance Generation**: Create random instances with customizable sizes and properties. See [`this tutorial`](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/03-Generating-New-Instances.ipynb).
30
30
  - **Gantt Charts**: Visualize final schedules and how they are created iteratively by dispatching rule solvers or sequences of scheduling decisions with GIFs or videos.
31
31
  - **Graph Representations**: Represent and visualize instances as disjunctive graphs or agent-task graphs (introduced in the ScheduleNet paper). Build your own custom graphs with the `JobShopGraph` class. See the [Disjunctive Graphs](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/04-Disjunctive-Graphs.ipynb) and [Resource Task Graphs](https://job-shop-lib.readthedocs.io/en/stable/examples/07-Resource-Task-Graph.html) examples.
32
32
 
@@ -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.6.0"
22
+ __version__ = "1.7.0"
23
23
 
24
24
  __all__ = [
25
25
  "Operation",
@@ -40,12 +40,12 @@ class JobShopInstance:
40
40
  num_machines
41
41
  num_operations
42
42
  is_flexible
43
- durations_matrix
43
+ duration_matrix
44
44
  machines_matrix
45
45
  release_dates_matrix
46
46
  deadlines_matrix
47
47
  due_dates_matrix
48
- durations_matrix_array
48
+ duration_matrix_array
49
49
  machines_matrix_array
50
50
  operations_by_machine
51
51
  max_duration
@@ -216,7 +216,7 @@ class JobShopInstance:
216
216
 
217
217
  {
218
218
  "name": self.name,
219
- "duration_matrix": self.durations_matrix,
219
+ "duration_matrix": self.duration_matrix,
220
220
  "machines_matrix": self.machines_matrix,
221
221
  "metadata": self.metadata,
222
222
  # Optionally (if the instance has them):
@@ -227,7 +227,7 @@ class JobShopInstance:
227
227
  """
228
228
  data = {
229
229
  "name": self.name,
230
- "duration_matrix": self.durations_matrix,
230
+ "duration_matrix": self.duration_matrix,
231
231
  "machines_matrix": self.machines_matrix,
232
232
  "metadata": self.metadata,
233
233
  }
@@ -374,6 +374,22 @@ class JobShopInstance:
374
374
 
375
375
  @functools.cached_property
376
376
  def durations_matrix(self) -> list[list[int]]:
377
+ """Another name for `duration_matrix` attribute, kept for
378
+ backward compatibility.
379
+
380
+ It may be removed in future versions.
381
+ """
382
+ warnings.warn(
383
+ "`duration_matrix` attribute is deprecated and will be "
384
+ "removed in future versions. Please use `duration_matrix` "
385
+ "property instead.",
386
+ DeprecationWarning,
387
+ stacklevel=2,
388
+ )
389
+ return self.duration_matrix
390
+
391
+ @functools.cached_property
392
+ def duration_matrix(self) -> list[list[int]]:
377
393
  """Returns the duration matrix of the instance.
378
394
 
379
395
  The duration of the operation with ``job_id`` i and ``position_in_job``
@@ -452,7 +468,23 @@ class JobShopInstance:
452
468
  If the jobs have different number of operations, the matrix is
453
469
  padded with ``np.nan`` to make it rectangular.
454
470
  """
455
- return self._fill_matrix_with_nans_2d(self.durations_matrix)
471
+ warnings.warn(
472
+ "`durations_matrix_array` attribute is deprecated and will be "
473
+ "removed in future versions. Please use `duration_matrix_array` "
474
+ "property instead.",
475
+ DeprecationWarning,
476
+ stacklevel=2,
477
+ )
478
+ return self.duration_matrix_array
479
+
480
+ @property
481
+ def duration_matrix_array(self) -> NDArray[np.float32]:
482
+ """Returns the duration matrix of the instance as a numpy array.
483
+
484
+ If the jobs have different number of operations, the matrix is
485
+ padded with ``np.nan`` to make it rectangular.
486
+ """
487
+ return self._fill_matrix_with_nans_2d(self.duration_matrix)
456
488
 
457
489
  @functools.cached_property
458
490
  def release_dates_matrix_array(self) -> NDArray[np.float32]:
@@ -405,10 +405,15 @@ class Schedule:
405
405
  critical_path = deque([last_scheduled_op])
406
406
  current_scheduled_op = last_scheduled_op
407
407
 
408
+ machine_op_index = {}
409
+ for machine_id, schedule_list in enumerate(self.schedule):
410
+ machine_op_index[machine_id] = {op: idx for idx, op in
411
+ enumerate(schedule_list)}
412
+
408
413
  # 2. Trace backwards from the last operation
409
414
  while True:
410
- job_pred = None
411
- machine_pred = None
415
+ job_pred: ScheduledOperation | None = None
416
+ machine_pred: ScheduledOperation | None = None
412
417
 
413
418
  # Find job predecessor (the previous operation in the same job)
414
419
  op_idx_in_job = current_scheduled_op.operation.position_in_job
@@ -423,9 +428,13 @@ class Schedule:
423
428
  # Find machine predecessor (the previous operation on the same
424
429
  # machine)
425
430
  machine_schedule = self.schedule[current_scheduled_op.machine_id]
426
- op_idx_on_machine = machine_schedule.index(current_scheduled_op)
431
+ op_idx_on_machine = (
432
+ machine_op_index
433
+ [current_scheduled_op.machine_id][current_scheduled_op])
427
434
  if op_idx_on_machine > 0:
428
- machine_pred = machine_schedule[op_idx_on_machine - 1]
435
+ machine_pred = machine_schedule[
436
+ op_idx_on_machine - 1
437
+ ]
429
438
 
430
439
  # 3. Determine the critical predecessor
431
440
  # The critical predecessor is the one that finished latest, as it
@@ -6,6 +6,7 @@
6
6
  load_all_benchmark_instances
7
7
  load_benchmark_instance
8
8
  load_benchmark_json
9
+ load_benchmark_group
9
10
 
10
11
  You can load a benchmark instance from the library:
11
12
 
@@ -93,10 +94,12 @@ from job_shop_lib.benchmarking._load_benchmark import (
93
94
  load_all_benchmark_instances,
94
95
  load_benchmark_instance,
95
96
  load_benchmark_json,
97
+ load_benchmark_group,
96
98
  )
97
99
 
98
100
  __all__ = [
99
101
  "load_all_benchmark_instances",
100
102
  "load_benchmark_instance",
101
103
  "load_benchmark_json",
104
+ "load_benchmark_group",
102
105
  ]
@@ -86,3 +86,28 @@ def load_benchmark_json() -> dict[str, dict[str, Any]]:
86
86
 
87
87
  with benchmark_file.open("r", encoding="utf-8") as f:
88
88
  return json.load(f)
89
+
90
+
91
+ @functools.cache
92
+ def load_benchmark_group(group_prefix: str) -> list[JobShopInstance]:
93
+ """Loads a group of benchmark instances whose names start with the given
94
+ prefix.
95
+
96
+ Args:
97
+ group_prefix:
98
+ The prefix of the benchmark instances to load. For example,
99
+ if the prefix is "la", all instances whose names start with "la"
100
+ (e.g., "la01-40", "la02-40", etc.) will be loaded.
101
+
102
+ Returns:
103
+ A list of :class:`JobShopInstance` objects whose names start with
104
+ the given prefix.
105
+
106
+ .. versionadded:: 1.7.0
107
+ """
108
+ all_instances = load_all_benchmark_instances()
109
+ return [
110
+ instance
111
+ for name, instance in all_instances.items()
112
+ if name.startswith(group_prefix)
113
+ ]
@@ -58,7 +58,7 @@ class DurationObserver(FeatureObserver):
58
58
  mapping[feature_type](scheduled_operation)
59
59
 
60
60
  def _initialize_operation_durations(self):
61
- duration_matrix = self.dispatcher.instance.durations_matrix_array
61
+ duration_matrix = self.dispatcher.instance.duration_matrix_array
62
62
  operation_durations = np.array(duration_matrix).reshape(-1, 1)
63
63
  # Drop the NaN values
64
64
  operation_durations = operation_durations[
@@ -80,7 +80,7 @@ class EarliestStartTimeObserver(FeatureObserver):
80
80
 
81
81
  # Earliest start times initialization
82
82
  # -------------------------------
83
- squared_duration_matrix = dispatcher.instance.durations_matrix_array
83
+ squared_duration_matrix = dispatcher.instance.duration_matrix_array
84
84
  self.earliest_start_times: NDArray[np.float32] = np.hstack(
85
85
  (
86
86
  np.zeros((squared_duration_matrix.shape[0], 1), dtype=float),
@@ -0,0 +1,71 @@
1
+ """Package for generating job shop instances.
2
+
3
+ .. autosummary::
4
+ :nosignatures:
5
+
6
+ InstanceGenerator
7
+ GeneralInstanceGenerator
8
+ modular_instance_generator
9
+ generate_machine_matrix_with_recirculation
10
+ generate_machine_matrix_without_recirculation
11
+ generate_duration_matrix
12
+ range_size_selector
13
+ choice_size_selector
14
+ get_default_machine_matrix_creator
15
+ get_default_duration_matrix_creator
16
+ ReleaseDateStrategy
17
+ create_release_dates_matrix
18
+ get_independent_release_date_strategy
19
+ get_cumulative_release_date_strategy
20
+ get_mixed_release_date_strategy
21
+ compute_horizon_proxy
22
+
23
+ """
24
+
25
+ from job_shop_lib.generation._size_selectors import (
26
+ range_size_selector,
27
+ choice_size_selector,
28
+ )
29
+ from job_shop_lib.generation._machine_matrix import (
30
+ generate_machine_matrix_with_recirculation,
31
+ generate_machine_matrix_without_recirculation,
32
+ get_default_machine_matrix_creator,
33
+ )
34
+ from job_shop_lib.generation._duration_matrix import (
35
+ get_default_duration_matrix_creator,
36
+ generate_duration_matrix,
37
+ )
38
+ from job_shop_lib.generation._release_date_matrix import (
39
+ ReleaseDateStrategy,
40
+ create_release_dates_matrix,
41
+ get_independent_release_date_strategy,
42
+ get_cumulative_release_date_strategy,
43
+ get_mixed_release_date_strategy,
44
+ compute_horizon_proxy,
45
+ )
46
+ from job_shop_lib.generation._modular_instance_generator import (
47
+ modular_instance_generator,
48
+ )
49
+ from job_shop_lib.generation._instance_generator import InstanceGenerator
50
+ from job_shop_lib.generation._general_instance_generator import (
51
+ GeneralInstanceGenerator,
52
+ )
53
+
54
+ __all__ = [
55
+ "InstanceGenerator",
56
+ "GeneralInstanceGenerator",
57
+ "generate_duration_matrix",
58
+ "generate_machine_matrix_with_recirculation",
59
+ "generate_machine_matrix_without_recirculation",
60
+ "modular_instance_generator",
61
+ "range_size_selector",
62
+ "choice_size_selector",
63
+ "get_default_machine_matrix_creator",
64
+ "get_default_duration_matrix_creator",
65
+ "ReleaseDateStrategy",
66
+ "create_release_dates_matrix",
67
+ "get_independent_release_date_strategy",
68
+ "get_cumulative_release_date_strategy",
69
+ "get_mixed_release_date_strategy",
70
+ "compute_horizon_proxy",
71
+ ]
@@ -0,0 +1,83 @@
1
+ from collections.abc import Callable
2
+ import random
3
+
4
+ import numpy as np
5
+ from numpy.typing import NDArray
6
+
7
+ from job_shop_lib.exceptions import ValidationError
8
+
9
+
10
+ def get_default_duration_matrix_creator(
11
+ duration_range: tuple[int, int] = (1, 99),
12
+ ) -> Callable[
13
+ [list[list[list[int]]] | list[list[int]], random.Random],
14
+ list[list[int]],
15
+ ]:
16
+ """Creates a duration matrix generator function.
17
+
18
+ Internally, it wraps :func:`generate_duration_matrix`.
19
+
20
+ .. note::
21
+
22
+ This function assumes that the machine matrix has the shape (num_jobs,
23
+ num_machines).
24
+
25
+ Args:
26
+ duration_range:
27
+ A tuple specifying the inclusive range for operation durations.
28
+
29
+ Returns:
30
+ A callable that generates a duration matrix of shape (num_jobs,
31
+ num_machines) when called with a machine matrix and a
32
+ `random.Random` instance.
33
+ """
34
+
35
+ def duration_matrix_creator(
36
+ machine_matrix: list[list[list[int]]] | list[list[int]],
37
+ rng: random.Random,
38
+ ) -> list[list[int]]:
39
+ seed_for_np = rng.randint(0, 2**16 - 1)
40
+ numpy_rng = np.random.default_rng(seed_for_np)
41
+ num_jobs = len(machine_matrix)
42
+ num_machines = len(machine_matrix[0])
43
+ return generate_duration_matrix(
44
+ num_jobs, num_machines, duration_range, numpy_rng
45
+ ).tolist()
46
+
47
+ return duration_matrix_creator
48
+
49
+
50
+ def generate_duration_matrix(
51
+ num_jobs: int,
52
+ num_machines: int,
53
+ duration_range: tuple[int, int],
54
+ rng: np.random.Generator | None = None,
55
+ ) -> NDArray[np.int32]:
56
+ """Generates a duration matrix.
57
+
58
+ Args:
59
+ num_jobs: The number of jobs.
60
+ num_machines: The number of machines.
61
+ duration_range: The range of the duration values.
62
+ rng: A numpy random number generator.
63
+
64
+ Returns:
65
+ A duration matrix with shape (num_jobs, num_machines).
66
+ """
67
+ rng = rng or np.random.default_rng()
68
+ if duration_range[0] > duration_range[1]:
69
+ raise ValidationError(
70
+ "The lower bound of the duration range must be less than or equal "
71
+ "to the upper bound."
72
+ )
73
+ if num_jobs <= 0:
74
+ raise ValidationError("The number of jobs must be greater than 0.")
75
+ if num_machines <= 0:
76
+ raise ValidationError("The number of machines must be greater than 0.")
77
+
78
+ return rng.integers(
79
+ duration_range[0],
80
+ duration_range[1] + 1,
81
+ size=(num_jobs, num_machines),
82
+ dtype=np.int32,
83
+ )
@@ -13,13 +13,13 @@ class InstanceGenerator(ABC):
13
13
  """Common interface for all generators.
14
14
 
15
15
  The class supports both single instance generation and iteration over
16
- multiple instances, controlled by the `iteration_limit` parameter. It
17
- implements the iterator protocol, allowing it to be used in a `for` loop.
16
+ multiple instances, controlled by the ``iteration_limit`` parameter. It
17
+ implements the iterator protocol, allowing it to be used in a ``for`` loop.
18
18
 
19
19
  Note:
20
20
  When used as an iterator, the generator will produce instances until it
21
- reaches the specified `iteration_limit`. If `iteration_limit` is None,
22
- it will continue indefinitely.
21
+ reaches the specified ``iteration_limit``. If ``iteration_limit`` is
22
+ ``None``, it will continue indefinitely.
23
23
 
24
24
  Attributes:
25
25
  num_jobs_range:
@@ -84,9 +84,11 @@ class InstanceGenerator(ABC):
84
84
  """Generates a single job shop instance
85
85
 
86
86
  Args:
87
- num_jobs: The number of jobs to generate. If None, a random value
87
+ num_jobs:
88
+ The number of jobs to generate. If None, a random value
88
89
  within the specified range will be used.
89
- num_machines: The number of machines to generate. If None, a random
90
+ num_machines:
91
+ The number of machines to generate. If None, a random
90
92
  value within the specified range will be used.
91
93
  """
92
94
 
@@ -1,43 +1,62 @@
1
+ from collections.abc import Callable
2
+ import random
3
+
1
4
  import numpy as np
2
5
  from numpy.typing import NDArray
3
6
 
4
7
  from job_shop_lib.exceptions import ValidationError
5
8
 
6
9
 
7
- def generate_duration_matrix(
8
- num_jobs: int,
9
- num_machines: int,
10
- duration_range: tuple[int, int],
11
- rng: np.random.Generator | None = None,
12
- ) -> NDArray[np.int32]:
13
- """Generates a duration matrix.
10
+ def get_default_machine_matrix_creator(
11
+ size_selector: Callable[[random.Random], tuple[int, int]] = (
12
+ lambda _: (10, 10)
13
+ ),
14
+ with_recirculation: bool = True,
15
+ ) -> Callable[[random.Random], list[list[list[int]]] | list[list[int]]]:
16
+ """Creates a machine matrix generator function.
17
+
18
+ Internally, it wraps either
19
+ :func:`generate_machine_matrix_with_recirculation`
20
+ or :func:`generate_machine_matrix_without_recirculation`
21
+ based on the `with_recirculation` parameter.
22
+
23
+ .. note::
24
+
25
+ The generated machine matrix will have the shape (num_jobs,
26
+ num_machines).
14
27
 
15
28
  Args:
16
- num_jobs: The number of jobs.
17
- num_machines: The number of machines.
18
- duration_range: The range of the duration values.
19
- rng: A numpy random number generator.
29
+ rng:
30
+ A random.Random instance.
31
+ size_selector:
32
+ A callable that takes a random.Random instance and returns a
33
+ tuple (num_jobs, num_machines).
34
+ with_recirculation:
35
+ If ``True``, generates a machine matrix with recirculation;
36
+ otherwise, without recirculation. Recirculation means that a job
37
+ can visit the same machine more than once.
20
38
 
21
39
  Returns:
22
- A duration matrix with shape (num_jobs, num_machines).
40
+ A callable that generates a machine matrix when called with a
41
+ random.Random instance.
23
42
  """
24
- rng = rng or np.random.default_rng()
25
- if duration_range[0] > duration_range[1]:
26
- raise ValidationError(
27
- "The lower bound of the duration range must be less than or equal "
28
- "to the upper bound."
29
- )
30
- if num_jobs <= 0:
31
- raise ValidationError("The number of jobs must be greater than 0.")
32
- if num_machines <= 0:
33
- raise ValidationError("The number of machines must be greater than 0.")
34
43
 
35
- return rng.integers(
36
- duration_range[0],
37
- duration_range[1] + 1,
38
- size=(num_jobs, num_machines),
39
- dtype=np.int32,
40
- )
44
+ def generator(
45
+ rng: random.Random,
46
+ ) -> list[list[list[int]]]:
47
+ num_jobs, num_machines = size_selector(rng)
48
+ seed_for_np = rng.randint(0, 2**16 - 1)
49
+ numpy_rng = np.random.default_rng(seed_for_np)
50
+ if with_recirculation:
51
+ return generate_machine_matrix_with_recirculation(
52
+ num_jobs, num_machines, numpy_rng
53
+ ).tolist()
54
+
55
+ return generate_machine_matrix_without_recirculation(
56
+ num_jobs, num_machines, numpy_rng
57
+ ).tolist()
58
+
59
+ return generator
41
60
 
42
61
 
43
62
  def generate_machine_matrix_with_recirculation(
@@ -51,8 +70,8 @@ def generate_machine_matrix_with_recirculation(
51
70
  rng: A numpy random number generator.
52
71
 
53
72
  Returns:
54
- A machine matrix with recirculation with shape (num_machines,
55
- num_jobs).
73
+ A machine matrix with recirculation with shape (num_jobs,
74
+ num_machines).
56
75
  """
57
76
  rng = rng or np.random.default_rng()
58
77
  if num_jobs <= 0:
@@ -62,7 +81,7 @@ def generate_machine_matrix_with_recirculation(
62
81
  num_machines_is_correct = False
63
82
  while not num_machines_is_correct:
64
83
  machine_matrix: np.ndarray = rng.integers(
65
- 0, num_machines, size=(num_machines, num_jobs), dtype=np.int32
84
+ 0, num_machines, size=(num_jobs, num_machines), dtype=np.int32
66
85
  )
67
86
  num_machines_is_correct = (
68
87
  len(np.unique(machine_matrix)) == num_machines
@@ -100,31 +119,3 @@ def generate_machine_matrix_without_recirculation(
100
119
  # Shuffle the columns:
101
120
  machine_matrix = np.apply_along_axis(rng.permutation, 1, machine_matrix)
102
121
  return machine_matrix
103
-
104
-
105
- if __name__ == "__main__":
106
-
107
- NUM_JOBS = 3
108
- NUM_MACHINES = 3
109
- DURATION_RANGE = (1, 10)
110
-
111
- duration_matrix = generate_duration_matrix(
112
- num_jobs=NUM_JOBS,
113
- num_machines=NUM_MACHINES,
114
- duration_range=DURATION_RANGE,
115
- )
116
- print(duration_matrix)
117
-
118
- machine_matrix_with_recirculation = (
119
- generate_machine_matrix_with_recirculation(
120
- num_jobs=NUM_JOBS, num_machines=NUM_MACHINES
121
- )
122
- )
123
- print(machine_matrix_with_recirculation)
124
-
125
- machine_matrix_without_recirculation = (
126
- generate_machine_matrix_without_recirculation(
127
- num_jobs=NUM_JOBS, num_machines=NUM_MACHINES
128
- )
129
- )
130
- print(machine_matrix_without_recirculation)