job-shop-lib 0.5.1__py3-none-any.whl → 1.0.0a1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. job_shop_lib/__init__.py +16 -8
  2. job_shop_lib/{base_solver.py → _base_solver.py} +1 -1
  3. job_shop_lib/{job_shop_instance.py → _job_shop_instance.py} +9 -4
  4. job_shop_lib/_operation.py +95 -0
  5. job_shop_lib/{schedule.py → _schedule.py} +73 -54
  6. job_shop_lib/{scheduled_operation.py → _scheduled_operation.py} +13 -37
  7. job_shop_lib/benchmarking/__init__.py +66 -43
  8. job_shop_lib/benchmarking/_load_benchmark.py +88 -0
  9. job_shop_lib/constraint_programming/__init__.py +13 -0
  10. job_shop_lib/{cp_sat/ortools_solver.py → constraint_programming/_ortools_solver.py} +57 -18
  11. job_shop_lib/dispatching/__init__.py +45 -41
  12. job_shop_lib/dispatching/{dispatcher.py → _dispatcher.py} +153 -80
  13. job_shop_lib/dispatching/_dispatcher_observer_config.py +54 -0
  14. job_shop_lib/dispatching/_factories.py +125 -0
  15. job_shop_lib/dispatching/{history_tracker.py → _history_observer.py} +4 -6
  16. job_shop_lib/dispatching/{pruning_functions.py → _ready_operation_filters.py} +6 -35
  17. job_shop_lib/dispatching/_unscheduled_operations_observer.py +69 -0
  18. job_shop_lib/dispatching/feature_observers/__init__.py +16 -10
  19. job_shop_lib/dispatching/feature_observers/{composite_feature_observer.py → _composite_feature_observer.py} +84 -2
  20. job_shop_lib/dispatching/feature_observers/{duration_observer.py → _duration_observer.py} +6 -17
  21. job_shop_lib/dispatching/feature_observers/{earliest_start_time_observer.py → _earliest_start_time_observer.py} +114 -35
  22. job_shop_lib/dispatching/feature_observers/{factory.py → _factory.py} +31 -5
  23. job_shop_lib/dispatching/feature_observers/{feature_observer.py → _feature_observer.py} +59 -16
  24. job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +97 -0
  25. job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +33 -0
  26. job_shop_lib/dispatching/feature_observers/{position_in_job_observer.py → _position_in_job_observer.py} +1 -8
  27. job_shop_lib/dispatching/feature_observers/{remaining_operations_observer.py → _remaining_operations_observer.py} +8 -26
  28. job_shop_lib/dispatching/rules/__init__.py +51 -0
  29. job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +82 -0
  30. job_shop_lib/dispatching/{dispatching_rule_solver.py → rules/_dispatching_rule_solver.py} +44 -15
  31. job_shop_lib/dispatching/{dispatching_rules.py → rules/_dispatching_rules_functions.py} +74 -21
  32. job_shop_lib/dispatching/rules/_machine_chooser_factory.py +69 -0
  33. job_shop_lib/dispatching/rules/_utils.py +127 -0
  34. job_shop_lib/exceptions.py +18 -0
  35. job_shop_lib/generation/__init__.py +2 -2
  36. job_shop_lib/generation/{general_instance_generator.py → _general_instance_generator.py} +26 -7
  37. job_shop_lib/generation/{instance_generator.py → _instance_generator.py} +13 -3
  38. job_shop_lib/graphs/__init__.py +17 -6
  39. job_shop_lib/graphs/{job_shop_graph.py → _job_shop_graph.py} +81 -2
  40. job_shop_lib/graphs/{node.py → _node.py} +18 -12
  41. job_shop_lib/graphs/graph_updaters/__init__.py +13 -0
  42. job_shop_lib/graphs/graph_updaters/_graph_updater.py +59 -0
  43. job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +154 -0
  44. job_shop_lib/graphs/graph_updaters/_utils.py +25 -0
  45. job_shop_lib/reinforcement_learning/__init__.py +41 -0
  46. job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +366 -0
  47. job_shop_lib/reinforcement_learning/_reward_observers.py +85 -0
  48. job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +337 -0
  49. job_shop_lib/reinforcement_learning/_types_and_constants.py +61 -0
  50. job_shop_lib/reinforcement_learning/_utils.py +96 -0
  51. job_shop_lib/visualization/__init__.py +20 -4
  52. job_shop_lib/visualization/{agent_task_graph.py → _agent_task_graph.py} +28 -9
  53. job_shop_lib/visualization/_gantt_chart_creator.py +219 -0
  54. job_shop_lib/visualization/_gantt_chart_video_and_gif_creation.py +388 -0
  55. {job_shop_lib-0.5.1.dist-info → job_shop_lib-1.0.0a1.dist-info}/METADATA +68 -44
  56. job_shop_lib-1.0.0a1.dist-info/RECORD +66 -0
  57. job_shop_lib/benchmarking/load_benchmark.py +0 -142
  58. job_shop_lib/cp_sat/__init__.py +0 -5
  59. job_shop_lib/dispatching/factories.py +0 -206
  60. job_shop_lib/dispatching/feature_observers/is_completed_observer.py +0 -98
  61. job_shop_lib/dispatching/feature_observers/is_ready_observer.py +0 -40
  62. job_shop_lib/generators/__init__.py +0 -8
  63. job_shop_lib/generators/basic_generator.py +0 -200
  64. job_shop_lib/generators/transformations.py +0 -164
  65. job_shop_lib/operation.py +0 -122
  66. job_shop_lib/visualization/create_gif.py +0 -209
  67. job_shop_lib-0.5.1.dist-info/RECORD +0 -52
  68. /job_shop_lib/dispatching/feature_observers/{is_scheduled_observer.py → _is_scheduled_observer.py} +0 -0
  69. /job_shop_lib/generation/{transformations.py → _transformations.py} +0 -0
  70. /job_shop_lib/graphs/{build_agent_task_graph.py → _build_agent_task_graph.py} +0 -0
  71. /job_shop_lib/graphs/{build_disjunctive_graph.py → _build_disjunctive_graph.py} +0 -0
  72. /job_shop_lib/graphs/{constants.py → _constants.py} +0 -0
  73. /job_shop_lib/visualization/{disjunctive_graph.py → _disjunctive_graph.py} +0 -0
  74. /job_shop_lib/visualization/{gantt_chart.py → _gantt_chart.py} +0 -0
  75. {job_shop_lib-0.5.1.dist-info → job_shop_lib-1.0.0a1.dist-info}/LICENSE +0 -0
  76. {job_shop_lib-0.5.1.dist-info → job_shop_lib-1.0.0a1.dist-info}/WHEEL +0 -0
job_shop_lib/__init__.py CHANGED
@@ -1,12 +1,22 @@
1
1
  """Contains the main data structures and base classes.
2
+
3
+ .. autosummary::
4
+ :nosignatures:
5
+
6
+ Operation
7
+ JobShopInstance
8
+ ScheduledOperation
9
+ Schedule
10
+ Solver
11
+ BaseSolver
12
+
2
13
  """
3
14
 
4
- from job_shop_lib.exceptions import JobShopLibError, NoSolutionFoundError
5
- from job_shop_lib.operation import Operation
6
- from job_shop_lib.job_shop_instance import JobShopInstance
7
- from job_shop_lib.scheduled_operation import ScheduledOperation
8
- from job_shop_lib.schedule import Schedule
9
- from job_shop_lib.base_solver import BaseSolver, Solver
15
+ from job_shop_lib._operation import Operation
16
+ from job_shop_lib._job_shop_instance import JobShopInstance
17
+ from job_shop_lib._scheduled_operation import ScheduledOperation
18
+ from job_shop_lib._schedule import Schedule
19
+ from job_shop_lib._base_solver import BaseSolver, Solver
10
20
 
11
21
 
12
22
  __all__ = [
@@ -16,6 +26,4 @@ __all__ = [
16
26
  "Schedule",
17
27
  "Solver",
18
28
  "BaseSolver",
19
- "JobShopLibError",
20
- "NoSolutionFoundError",
21
29
  ]
@@ -33,5 +33,5 @@ class BaseSolver(abc.ABC):
33
33
  schedule = self.solve(instance)
34
34
  elapsed_time = time_start - time.perf_counter()
35
35
  schedule.metadata["elapsed_time"] = elapsed_time
36
- schedule.metadata["solved_by"] = f"{self.__class__.__name__}"
36
+ schedule.metadata["solved_by"] = self.__class__.__name__
37
37
  return schedule
@@ -7,6 +7,7 @@ import functools
7
7
  from typing import Any
8
8
 
9
9
  import numpy as np
10
+ from numpy.typing import NDArray
10
11
 
11
12
  from job_shop_lib import Operation
12
13
 
@@ -267,7 +268,7 @@ class JobShopInstance:
267
268
  ]
268
269
 
269
270
  @functools.cached_property
270
- def durations_matrix_array(self) -> np.ndarray:
271
+ def durations_matrix_array(self) -> NDArray[np.float32]:
271
272
  """Returns the duration matrix of the instance as a numpy array.
272
273
 
273
274
  The returned array has shape (num_jobs, max_num_operations_per_job).
@@ -284,7 +285,7 @@ class JobShopInstance:
284
285
  return self._fill_matrix_with_nans_2d(duration_matrix)
285
286
 
286
287
  @functools.cached_property
287
- def machines_matrix_array(self) -> np.ndarray:
288
+ def machines_matrix_array(self) -> NDArray[np.float32]:
288
289
  """Returns the machines matrix of the instance as a numpy array.
289
290
 
290
291
  The returned array has shape (num_jobs, max_num_operations_per_job,
@@ -409,7 +410,9 @@ class JobShopInstance:
409
410
  return sum(self.job_durations)
410
411
 
411
412
  @staticmethod
412
- def _fill_matrix_with_nans_2d(matrix: list[list[int]]) -> np.ndarray:
413
+ def _fill_matrix_with_nans_2d(
414
+ matrix: list[list[int]],
415
+ ) -> NDArray[np.float32]:
413
416
  """Fills a matrix with np.nan values.
414
417
 
415
418
  Args:
@@ -429,7 +432,9 @@ class JobShopInstance:
429
432
  return squared_matrix
430
433
 
431
434
  @staticmethod
432
- def _fill_matrix_with_nans_3d(matrix: list[list[list[int]]]) -> np.ndarray:
435
+ def _fill_matrix_with_nans_3d(
436
+ matrix: list[list[list[int]]],
437
+ ) -> NDArray[np.float32]:
433
438
  """Fills a 3D matrix with np.nan values.
434
439
 
435
440
  Args:
@@ -0,0 +1,95 @@
1
+ """Home of the `Operation` class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from job_shop_lib.exceptions import UninitializedAttributeError
6
+
7
+
8
+ class Operation:
9
+ """Stores machine and duration information for a job operation.
10
+
11
+ An operation is a task that must be performed on a machine. It is part of a
12
+ job and has a duration that represents the time it takes to complete the
13
+ task.
14
+
15
+ Tip:
16
+ To use custom attributes, such as due dates or priorities, subclass
17
+ this class and add the desired attributes.
18
+
19
+ Note:
20
+ To increase performance, some solvers such as the CP-SAT solver use
21
+ only integers to represent the operation's attributes. Should a
22
+ problem involve operations with non-integer durations, it would be
23
+ necessary to multiply all durations by a sufficiently large integer so
24
+ that every duration is an integer.
25
+
26
+ Attributes:
27
+ machines: A list of machine ids that can perform the operation.
28
+ duration: The time it takes to perform the operation.
29
+ """
30
+
31
+ __slots__ = (
32
+ "machines",
33
+ "duration",
34
+ "job_id",
35
+ "position_in_job",
36
+ "operation_id",
37
+ )
38
+
39
+ def __init__(self, machines: int | list[int], duration: int):
40
+ """Initializes the object with the given machines and duration.
41
+
42
+ Args:
43
+ machines:
44
+ A list of machine ids that can perform the operation. If
45
+ only one machine can perform the operation, it can be passed as
46
+ an integer.
47
+ duration:
48
+ The time it takes to perform the operation.
49
+ """
50
+ self.machines = [machines] if isinstance(machines, int) else machines
51
+ self.duration = duration
52
+
53
+ # Defined outside the class by the JobShopInstance class:
54
+ self.job_id: int = -1
55
+ self.position_in_job: int = -1
56
+ self.operation_id: int = -1
57
+
58
+ @property
59
+ def machine_id(self) -> int:
60
+ """Returns the id of the machine associated with the operation.
61
+
62
+ Raises:
63
+ UninitializedAttributeError: If the operation has multiple machines
64
+ in its list.
65
+ """
66
+ if len(self.machines) > 1:
67
+ raise UninitializedAttributeError(
68
+ "Operation has multiple machines."
69
+ )
70
+ return self.machines[0]
71
+
72
+ def is_initialized(self) -> bool:
73
+ """Returns whether the operation has been initialized."""
74
+ return (
75
+ self.job_id == -1
76
+ or self.position_in_job == -1
77
+ or self.operation_id == -1
78
+ )
79
+
80
+ def __hash__(self) -> int:
81
+ return hash(self.operation_id)
82
+
83
+ def __eq__(self, value: object) -> bool:
84
+ if not isinstance(value, Operation):
85
+ return False
86
+ return self.__slots__ == value.__slots__
87
+
88
+ def __repr__(self) -> str:
89
+ machines = (
90
+ self.machines[0] if len(self.machines) == 1 else self.machines
91
+ )
92
+ return (
93
+ f"O(m={machines}, d={self.duration}, "
94
+ f"j={self.job_id}, p={self.position_in_job})"
95
+ )
@@ -5,19 +5,30 @@ from __future__ import annotations
5
5
  from typing import Any
6
6
  from collections import deque
7
7
 
8
- from job_shop_lib import ScheduledOperation, JobShopInstance, JobShopLibError
8
+ from job_shop_lib import ScheduledOperation, JobShopInstance
9
+ from job_shop_lib.exceptions import ValidationError
9
10
 
10
11
 
11
12
  class Schedule:
12
- """Data structure to store a schedule for a `JobShopInstance` object.
13
+ """Data structure to store a complete or partial solution for a particular
14
+ :class:`JobShopInstance`.
15
+
16
+ A schedule is a list of lists of :class:`ScheduledOperation` objects. Each
17
+ list represents the order of operations on a machine.
18
+
19
+ The main methods of this class are:
20
+
21
+ .. autosummary::
22
+ :nosignatures:
23
+
24
+ makespan
25
+ is_complete
26
+ add
27
+ reset
13
28
 
14
29
  Attributes:
15
30
  instance:
16
- The `JobShopInstance` object that the schedule is for.
17
- schedule:
18
- A list of lists of `ScheduledOperation` objects. Each list of
19
- `ScheduledOperation` objects represents the order of operations
20
- on a machine.
31
+ The :class:`JobShopInstance` object that the schedule is for.
21
32
  metadata:
22
33
  A dictionary with additional information about the schedule. It
23
34
  can be used to store information about the algorithm that generated
@@ -40,12 +51,11 @@ class Schedule:
40
51
 
41
52
  Args:
42
53
  instance:
43
- The `JobShopInstance` object that the schedule is for.
54
+ The :class:`JobShopInstance` object that the schedule is for.
44
55
  schedule:
45
- A list of lists of `ScheduledOperation` objects. Each list of
46
- `ScheduledOperation` objects represents the order of operations
47
- on a machine. If not provided, the schedule is initialized as
48
- an empty schedule.
56
+ A list of lists of :class:`ScheduledOperation` objects. Each
57
+ list represents the order of operations on a machine. If
58
+ not provided, the schedule is initialized as an empty schedule.
49
59
  **metadata:
50
60
  Additional information about the schedule.
51
61
  """
@@ -63,7 +73,8 @@ class Schedule:
63
73
 
64
74
  @property
65
75
  def schedule(self) -> list[list[ScheduledOperation]]:
66
- """Returns the schedule attribute."""
76
+ """A list of lists of :class:`ScheduledOperation` objects. Each list
77
+ represents the order of operations on a machine."""
67
78
  return self._schedule
68
79
 
69
80
  @schedule.setter
@@ -73,7 +84,7 @@ class Schedule:
73
84
 
74
85
  @property
75
86
  def num_scheduled_operations(self) -> int:
76
- """Returns the number of operations that have been scheduled."""
87
+ """The number of operations that have been scheduled so far."""
77
88
  return sum(len(machine_schedule) for machine_schedule in self.schedule)
78
89
 
79
90
  def to_dict(self) -> dict:
@@ -84,13 +95,14 @@ class Schedule:
84
95
  Returns:
85
96
  A dictionary representation of the schedule with the following
86
97
  keys:
87
- - "instance": A dictionary representation of the instance.
88
- - "job_sequences": A list of lists of job ids. Each list of job
89
- ids represents the order of operations on the machine. The
90
- machine that the list corresponds to is determined by the
91
- index of the list.
92
- - "metadata": A dictionary with additional information about
93
- the schedule.
98
+
99
+ - **"instance"**: A dictionary representation of the instance.
100
+ - **"job_sequences"**: A list of lists of job ids. Each list
101
+ of job ids represents the order of operations on the machine.
102
+ The machine that the list corresponds to is determined by the
103
+ index of the list.
104
+ - **"metadata"**: A dictionary with additional information
105
+ about the schedule.
94
106
  """
95
107
  job_sequences: list[list[int]] = []
96
108
  for machine_schedule in self.schedule:
@@ -131,14 +143,14 @@ class Schedule:
131
143
 
132
144
  Args:
133
145
  instance:
134
- The `JobShopInstance` object that the schedule is for.
146
+ The :class:`JobShopInstance` object that the schedule is for.
135
147
  job_sequences:
136
148
  A list of lists of job ids. Each list of job ids represents the
137
149
  order of operations on the machine. The machine that the list
138
150
  corresponds to is determined by the index of the list.
139
151
 
140
152
  Returns:
141
- A `Schedule` object with the given job sequences.
153
+ A :class:`Schedule` object with the given job sequences.
142
154
  """
143
155
  from job_shop_lib.dispatching import Dispatcher
144
156
 
@@ -161,7 +173,7 @@ class Schedule:
161
173
  at_least_one_operation_scheduled = True
162
174
 
163
175
  if not at_least_one_operation_scheduled:
164
- raise JobShopLibError(
176
+ raise ValidationError(
165
177
  "Invalid job sequences. No valid operation to schedule."
166
178
  )
167
179
  return dispatcher.schedule
@@ -182,18 +194,19 @@ class Schedule:
182
194
  return max_end_time
183
195
 
184
196
  def is_complete(self) -> bool:
185
- """Returns True if all operations have been scheduled."""
197
+ """Returns ``True`` if all operations have been scheduled."""
186
198
  return self.num_scheduled_operations == self.instance.num_operations
187
199
 
188
200
  def add(self, scheduled_operation: ScheduledOperation):
189
- """Adds a new `ScheduledOperation` to the schedule.
201
+ """Adds a new :class:`ScheduledOperation` to the schedule.
190
202
 
191
203
  Args:
192
204
  scheduled_operation:
193
- The `ScheduledOperation` to add to the schedule.
205
+ The :class:`ScheduledOperation` to add to the schedule.
194
206
 
195
207
  Raises:
196
- ValueError: If the start time of the new operation is before the
208
+ ValidationError:
209
+ If the start time of the new operation is before the
197
210
  end time of the last operation on the same machine. In favor of
198
211
  performance, this method does not checks precedence
199
212
  constraints.
@@ -212,49 +225,45 @@ class Schedule:
212
225
  return
213
226
 
214
227
  last_operation = self.schedule[new_operation.machine_id][-1]
215
- self._check_start_time(new_operation, last_operation)
228
+ if not self._is_valid_start_time(new_operation, last_operation):
229
+ raise ValidationError(
230
+ "Operation cannot be scheduled before the last operation on "
231
+ "the same machine: end time of last operation "
232
+ f"({last_operation.end_time}) > start time of new operation "
233
+ f"({new_operation.start_time})."
234
+ )
216
235
 
217
236
  @staticmethod
218
- def _check_start_time(
237
+ def _is_valid_start_time(
219
238
  scheduled_operation: ScheduledOperation,
220
239
  previous_operation: ScheduledOperation,
221
240
  ):
222
- """Raises a ValueError if the start time of the new operation is before
223
- the end time of the last operation on the same machine."""
224
-
225
- if previous_operation.end_time <= scheduled_operation.start_time:
226
- return
227
-
228
- raise ValueError(
229
- "Operation cannot be scheduled before the last operation on "
230
- "the same machine: end time of last operation "
231
- f"({previous_operation.end_time}) > start time of new operation "
232
- f"({scheduled_operation.start_time})."
233
- )
241
+ return previous_operation.end_time <= scheduled_operation.start_time
234
242
 
235
243
  @staticmethod
236
244
  def check_schedule(schedule: list[list[ScheduledOperation]]):
237
- """Checks if a schedule is valid and raises a ValueError if it is not.
245
+ """Checks if a schedule is valid and raises a
246
+ :class:`~exceptions.ValidationError` if it is not.
238
247
 
239
248
  A schedule is considered invalid if:
240
- - A `ScheduledOperation` has a machine id that does not match the
241
- machine id of the machine schedule (the list of
242
- `ScheduledOperation` objects) that it belongs to.
243
- - The start time of a `ScheduledOperation` is before the end time
244
- of the last operation on the same machine.
249
+ - A :class:`ScheduledOperation` has a machine id that does not
250
+ match the machine id of the machine schedule (the list of
251
+ :class:`ScheduledOperation` objects) that it belongs to.
252
+ - The start time of a :class:`ScheduledOperation` is before the
253
+ end time of the last operation on the same machine.
245
254
 
246
255
  Args:
247
256
  schedule:
248
- The schedule (a list of lists of `ScheduledOperation` objects)
249
- to check.
257
+ The schedule (a list of lists of :class:`ScheduledOperation`
258
+ objects) to check.
250
259
 
251
260
  Raises:
252
- ValueError: If the schedule is invalid.
261
+ ValidationError: If the schedule is invalid.
253
262
  """
254
263
  for machine_id, scheduled_operations in enumerate(schedule):
255
264
  for i, scheduled_operation in enumerate(scheduled_operations):
256
265
  if scheduled_operation.machine_id != machine_id:
257
- raise ValueError(
266
+ raise ValidationError(
258
267
  "The machine id of the scheduled operation "
259
268
  f"({ScheduledOperation.machine_id}) does not match "
260
269
  f"the machine id of the machine schedule ({machine_id}"
@@ -264,9 +273,19 @@ class Schedule:
264
273
  if i == 0:
265
274
  continue
266
275
 
267
- Schedule._check_start_time(
276
+ if not Schedule._is_valid_start_time(
268
277
  scheduled_operation, scheduled_operations[i - 1]
269
- )
278
+ ):
279
+ raise ValidationError(
280
+ "Invalid schedule. The start time of the new "
281
+ "operation is before the end time of the last "
282
+ "operation on the same machine."
283
+ "End time of last operation: "
284
+ f"{scheduled_operations[i - 1].end_time}. "
285
+ f"Start time of new operation: "
286
+ f"{scheduled_operation.start_time}. At index "
287
+ f"[{machine_id}][{i}]."
288
+ )
270
289
 
271
290
  def __eq__(self, value: object) -> bool:
272
291
  if not isinstance(value, Schedule):
@@ -1,8 +1,7 @@
1
1
  """Home of the `ScheduledOperation` class."""
2
2
 
3
- from warnings import warn
4
-
5
- from job_shop_lib import Operation, JobShopLibError
3
+ from job_shop_lib import Operation
4
+ from job_shop_lib.exceptions import ValidationError
6
5
 
7
6
 
8
7
  class ScheduledOperation:
@@ -10,30 +9,29 @@ class ScheduledOperation:
10
9
 
11
10
  Attributes:
12
11
  operation:
13
- The `Operation` object that is scheduled.
12
+ The :class:`Operation` object that is scheduled.
14
13
  start_time:
15
14
  The time at which the operation is scheduled to start.
16
- machine_id:
17
- The id of the machine on which the operation is scheduled.
15
+
18
16
  """
19
17
 
20
18
  __slots__ = ("operation", "start_time", "_machine_id")
21
19
 
22
20
  def __init__(self, operation: Operation, start_time: int, machine_id: int):
23
- """Initializes the object with the given operation, start time, and
24
- machine id.
21
+ """Initializes a new instance of the :class:`ScheduledOperation` class.
25
22
 
26
23
  Args:
27
24
  operation:
28
- The `Operation` object that is scheduled.
25
+ The :class:`Operation` object that is scheduled.
29
26
  start_time:
30
27
  The time at which the operation is scheduled to start.
31
28
  machine_id:
32
29
  The id of the machine on which the operation is scheduled.
33
30
 
34
31
  Raises:
35
- ValueError:
36
- If the machine_id is not valid for the operation.
32
+ ValidationError:
33
+ If the given machine_id is not in the list of valid machines
34
+ for the operation.
37
35
  """
38
36
  self.operation = operation
39
37
  self.start_time = start_time
@@ -49,7 +47,7 @@ class ScheduledOperation:
49
47
  @machine_id.setter
50
48
  def machine_id(self, value: int):
51
49
  if value not in self.operation.machines:
52
- raise JobShopLibError(
50
+ raise ValidationError(
53
51
  f"Operation cannot be scheduled on machine {value}. "
54
52
  f"Valid machines are {self.operation.machines}."
55
53
  )
@@ -57,35 +55,13 @@ class ScheduledOperation:
57
55
 
58
56
  @property
59
57
  def job_id(self) -> int:
60
- """Returns the id of the job that the operation belongs to.
61
-
62
- Raises:
63
- ValueError: If the operation has no job_id.
64
- """
65
-
66
- if self.operation.job_id is None:
67
- raise JobShopLibError("Operation has no job_id.")
58
+ """Returns the id of the job that the operation belongs to."""
68
59
  return self.operation.job_id
69
60
 
70
- @property
71
- def position(self) -> int:
72
- """Deprecated. Use `position_in_job` instead."""
73
- warn(
74
- "The `position` attribute is deprecated. Use `position_in_job` "
75
- "instead. It will be removed in version 1.0.0.",
76
- DeprecationWarning,
77
- )
78
- return self.position_in_job
79
-
80
61
  @property
81
62
  def position_in_job(self) -> int:
82
- """Returns the position (starting at zero) of the operation in the job.
83
-
84
- Raises:
85
- ValueError: If the operation has no position_in_job.
86
- """
87
- if self.operation.position_in_job is None:
88
- raise JobShopLibError("Operation has no position.")
63
+ """Returns the position (starting at zero) of the operation in the
64
+ job."""
89
65
  return self.operation.position_in_job
90
66
 
91
67
  @property
@@ -1,71 +1,94 @@
1
- """Package for loading benchmark instances.
1
+ """Contains functions to load benchmark instances.
2
2
 
3
- All benchmark instances are stored in a single JSON file. This module provides
4
- functions to load the instances from the file and return them as
5
- JobShopInstance objects.
3
+ .. autosummary::
4
+
5
+ load_all_benchmark_instances
6
+ load_benchmark_instance
7
+ load_benchmark_json
8
+
9
+ You can load a benchmark instance from the library:
10
+
11
+ .. code-block:: python
12
+
13
+ from job_shop_lib.benchmarking import load_benchmark_instance
14
+
15
+ ft06 = load_benchmark_instance("ft06")
16
+
17
+
18
+ This package contains functions to load the instances from the file
19
+ and return them as :class:`JobShopInstance` objects without having to download
20
+ them manually.
6
21
 
7
22
  The contributions to this benchmark dataset are as follows:
8
23
 
9
- abz5-9: This subset, comprising five instances, was introduced by Adams et
10
- al. (1988).
11
- ft06, ft10, ft20: These three instances are attributed to the work of
12
- Fisher and Thompson, as detailed in their 1963 work.
13
- la01-40: A collection of forty instances, this group was contributed by
14
- Lawrence, as referenced in his 1984 report.
15
- orb01-10: Ten instances in this category were provided by Applegate and
16
- Cook, as seen in their 1991 study.
17
- swb01-20: This segment, encompassing twenty instances, was contributed by
18
- Storer et al., as per their 1992 article.
19
- yn1-4: Yamada and Nakano are credited with the addition of four instances
20
- in this group, as found in their 1992 paper.
21
- ta01-80: The largest contribution, consisting of eighty instances, was
22
- made by Taillard, as documented in his 1993 paper.
24
+ - ``abz5-9``: by Adams et al. (1988).
23
25
 
24
- The metadata from these instances has been updated using data from:
26
+ - ``ft06``, ``ft10``, ``ft20``: by Fisher and Thompson (1963).
27
+
28
+ - ``la01-40``: by Lawrence (1984)
25
29
 
26
- Thomas Weise. jsspInstancesAndResults. Accessed in January 2024.
27
- Available at: https://github.com/thomasWeise/jsspInstancesAndResults
30
+ - ``orb01-10``: by Applegate and Cook (1991).
31
+
32
+ - ``swb01-20``: by Storer et al. (1992).
33
+
34
+ - ``yn1-4``: by Yamada and Nakano (1992).
35
+
36
+ - ``ta01-80``: by Taillard (1993).
37
+
38
+ The metadata from these instances has been updated using data from:
39
+ https://github.com/thomasWeise/jsspInstancesAndResults
28
40
 
29
41
  It includes the following information:
30
- - "optimum" (int | None): The optimal makespan for the instance.
31
- - "lower_bound" (int): The lower bound for the makespan. If
32
- optimality is known, it is equal to the optimum.
33
- - "upper_bound" (int): The upper bound for the makespan. If
34
- optimality is known, it is equal to the optimum.
35
- - "reference" (str): The paper or source where the instance was first
36
- introduced.
42
+ - "optimum" (``int | None``): The optimal makespan for the instance.
43
+ - "lower_bound" (``int``): The lower bound for the makespan. If
44
+ optimality is known, it is equal to the optimum.
45
+ - "upper_bound" (``int``): The upper bound for the makespan. If
46
+ optimality is known, it is equal to the optimum.
47
+ - "reference" (``str``): The paper or source where the instance was first
48
+ introduced.
49
+
50
+ .. code-block:: python
51
+
52
+ >>> ft06.metadata
53
+ {'optimum': 55,
54
+ 'upper_bound': 55,
55
+ 'lower_bound': 55,
56
+ 'reference': "J.F. Muth, G.L. Thompson. 'Industrial scheduling.',
57
+ Englewood Cliffs, NJ, Prentice-Hall, 1963."}
37
58
 
38
59
  References:
60
+
39
61
  - J. Adams, E. Balas, and D. Zawack, "The shifting bottleneck procedure
40
- for job shop scheduling," Management Science, vol. 34, no. 3,
41
- pp. 391–401, 1988.
62
+ for job shop scheduling," Management Science, vol. 34, no. 3,
63
+ pp. 391–401, 1988.
42
64
 
43
65
  - J.F. Muth and G.L. Thompson, Industrial scheduling. Englewood Cliffs,
44
- NJ: Prentice-Hall, 1963.
66
+ NJ: Prentice-Hall, 1963.
45
67
 
46
68
  - S. Lawrence, "Resource constrained project scheduling: An experimental
47
- investigation of heuristic scheduling techniques (Supplement),"
48
- Carnegie-Mellon University, Graduate School of Industrial
49
- Administration, Pittsburgh, Pennsylvania, 1984.
69
+ investigation of heuristic scheduling techniques (Supplement),"
70
+ Carnegie-Mellon University, Graduate School of Industrial
71
+ Administration, Pittsburgh, Pennsylvania, 1984.
50
72
 
51
73
  - D. Applegate and W. Cook, "A computational study of job-shop
52
- scheduling," ORSA Journal on Computer, vol. 3, no. 2, pp. 149–156,
53
- 1991.
74
+ scheduling," ORSA Journal on Computer, vol. 3, no. 2, pp. 149–156,
75
+ 1991.
54
76
 
55
77
  - R.H. Storer, S.D. Wu, and R. Vaccari, "New search spaces for
56
- sequencing problems with applications to job-shop scheduling,"
57
- Management Science, vol. 38, no. 10, pp. 1495–1509, 1992.
78
+ sequencing problems with applications to job-shop scheduling,"
79
+ Management Science, vol. 38, no. 10, pp. 1495–1509, 1992.
58
80
 
59
81
  - T. Yamada and R. Nakano, "A genetic algorithm applicable to
60
- large-scale job-shop problems," in Proceedings of the Second
61
- International Workshop on Parallel Problem Solving from Nature
62
- (PPSN'2), Brussels, Belgium, pp. 281–290, 1992.
82
+ large-scale job-shop problems," in Proceedings of the Second
83
+ International Workshop on Parallel Problem Solving from Nature
84
+ (PPSN'2), Brussels, Belgium, pp. 281–290, 1992.
63
85
 
64
86
  - E. Taillard, "Benchmarks for basic scheduling problems," European
65
- Journal of Operational Research, vol. 64, no. 2, pp. 278–285, 1993.
87
+ Journal of Operational Research, vol. 64, no. 2, pp. 278–285, 1993.
88
+
66
89
  """
67
90
 
68
- from job_shop_lib.benchmarking.load_benchmark import (
91
+ from job_shop_lib.benchmarking._load_benchmark import (
69
92
  load_all_benchmark_instances,
70
93
  load_benchmark_instance,
71
94
  load_benchmark_json,