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.
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/PKG-INFO +4 -5
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/README.md +2 -2
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/__init__.py +1 -1
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_job_shop_instance.py +37 -5
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_schedule.py +13 -4
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/benchmarking/__init__.py +3 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/benchmarking/_load_benchmark.py +25 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +1 -1
- {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
- job_shop_lib-1.7.0/job_shop_lib/generation/__init__.py +71 -0
- job_shop_lib-1.7.0/job_shop_lib/generation/_duration_matrix.py +83 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/generation/_instance_generator.py +8 -6
- 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
- job_shop_lib-1.7.0/job_shop_lib/generation/_modular_instance_generator.py +116 -0
- job_shop_lib-1.7.0/job_shop_lib/generation/_release_date_matrix.py +160 -0
- job_shop_lib-1.7.0/job_shop_lib/generation/_size_selectors.py +58 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/__init__.py +6 -0
- job_shop_lib-1.7.0/job_shop_lib/metaheuristics/_objective_functions.py +114 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/__init__.py +9 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_reward_observers.py +76 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_utils.py +63 -4
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/pyproject.toml +2 -5
- job_shop_lib-1.6.0/job_shop_lib/generation/__init__.py +0 -30
- job_shop_lib-1.6.0/job_shop_lib/metaheuristics/_objective_functions.py +0 -73
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/LICENSE +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_base_solver.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_operation.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/_scheduled_operation.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/constraint_programming/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/constraint_programming/_ortools_solver.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_dispatcher.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_dispatcher_observer_config.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_factories.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_history_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_optimal_operations_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_ready_operation_filters.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_start_time_calculators.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_dates_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_factory.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
- {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
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/dispatching/rules/_utils.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/exceptions.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/generation/_general_instance_generator.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_build_disjunctive_graph.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_build_resource_task_graphs.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_constants.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_job_shop_graph.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/_node.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/graphs/graph_updaters/_utils.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/_job_shop_annealer.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/_neighbor_generators.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/metaheuristics/_simulated_annealing_solver.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/py.typed +0 -0
- {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
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py +0 -0
- {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
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/reinforcement_learning/_types_and_constants.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/gantt/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/gantt/_gantt_chart_creator.py +0 -0
- {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
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/gantt/_plot_gantt_chart.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/graphs/__init__.py +0 -0
- {job_shop_lib-1.6.0 → job_shop_lib-1.7.0}/job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py +0 -0
- {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.
|
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,<
|
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/
|
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 [`
|
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/
|
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 [`
|
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
|
|
@@ -40,12 +40,12 @@ class JobShopInstance:
|
|
40
40
|
num_machines
|
41
41
|
num_operations
|
42
42
|
is_flexible
|
43
|
-
|
43
|
+
duration_matrix
|
44
44
|
machines_matrix
|
45
45
|
release_dates_matrix
|
46
46
|
deadlines_matrix
|
47
47
|
due_dates_matrix
|
48
|
-
|
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.
|
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.
|
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
|
-
|
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 =
|
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[
|
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.
|
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.
|
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
|
17
|
-
implements the iterator protocol, allowing it to be used in a
|
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
|
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:
|
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:
|
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
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
) ->
|
13
|
-
"""
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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 (
|
55
|
-
|
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=(
|
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)
|