job-shop-lib 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
job_shop_lib/__init__.py CHANGED
@@ -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.2.0"
22
+ __version__ = "1.3.0"
23
23
 
24
24
  __all__ = [
25
25
  "Operation",
job_shop_lib/_schedule.py CHANGED
@@ -2,12 +2,15 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any
5
+ from typing import Any, TYPE_CHECKING
6
6
  from collections import deque
7
7
 
8
8
  from job_shop_lib import ScheduledOperation, JobShopInstance
9
9
  from job_shop_lib.exceptions import ValidationError
10
10
 
11
+ if TYPE_CHECKING:
12
+ from job_shop_lib.dispatching import Dispatcher
13
+
11
14
 
12
15
  class Schedule:
13
16
  r"""Data structure to store a complete or partial solution for a particular
@@ -148,6 +151,7 @@ class Schedule:
148
151
  def from_job_sequences(
149
152
  instance: JobShopInstance,
150
153
  job_sequences: list[list[int]],
154
+ dispatcher: Dispatcher | None = None,
151
155
  ) -> Schedule:
152
156
  """Creates an active schedule from a list of job sequences.
153
157
 
@@ -163,13 +167,27 @@ class Schedule:
163
167
  A list of lists of job ids. Each list of job ids represents the
164
168
  order of operations on the machine. The machine that the list
165
169
  corresponds to is determined by the index of the list.
170
+ dispatcher:
171
+ A :class:`~job_shop_lib.dispatching.Dispatcher` to use for
172
+ scheduling. If not provided, a new dispatcher will be
173
+ created.
174
+
175
+ .. note::
176
+ You will need to provide a dispatcher if you want to
177
+ take into account start time calculators different from
178
+ the default one.
166
179
 
167
180
  Returns:
168
181
  A :class:`Schedule` object with the given job sequences.
182
+
183
+ .. seealso::
184
+ See :mod:`job_shop_lib.dispatching` for more information
185
+ about dispatchers and the start time calculators available.
169
186
  """
170
187
  from job_shop_lib.dispatching import Dispatcher
171
188
 
172
- dispatcher = Dispatcher(instance)
189
+ if dispatcher is None:
190
+ dispatcher = Dispatcher(instance)
173
191
  dispatcher.reset()
174
192
  raw_solution_deques = [deque(job_ids) for job_ids in job_sequences]
175
193
 
@@ -307,3 +325,11 @@ class Schedule:
307
325
  return False
308
326
 
309
327
  return self.schedule == value.schedule
328
+
329
+ def copy(self) -> Schedule:
330
+ """Returns a copy of the schedule."""
331
+ return Schedule(
332
+ self.instance,
333
+ [machine_schedule.copy() for machine_schedule in self.schedule],
334
+ **self.metadata,
335
+ )
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any
5
+ from typing import Any, Sequence
6
6
  import time
7
7
 
8
8
  from ortools.sat.python import cp_model
@@ -64,14 +64,28 @@ class ORToolsSolver(BaseSolver):
64
64
  self.solver = cp_model.CpSolver()
65
65
  self._operations_start: dict[Operation, tuple[IntVar, IntVar]] = {}
66
66
 
67
- def __call__(self, instance: JobShopInstance) -> Schedule:
67
+ def __call__(
68
+ self,
69
+ instance: JobShopInstance,
70
+ arrival_times: Sequence[Sequence[int]] | None = None,
71
+ deadlines: Sequence[Sequence[int]] | None = None,
72
+ ) -> Schedule:
68
73
  """Equivalent to calling the :meth:`~ORToolsSolver.solve` method.
69
74
 
70
75
  This method is necessary because, in JobShopLib, solvers are defined
71
76
  as callables that receive an instance and return a schedule.
72
77
 
73
78
  Args:
74
- instance: The job shop instance to be solved.
79
+ instance:
80
+ The job shop instance to be solved.
81
+ arrival_times:
82
+ Optional arrival times for each operation.
83
+ If provided, the solver will ensure that operations do not
84
+ start before their respective arrival times.
85
+ deadlines:
86
+ Optional deadlines for each operation.
87
+ If provided, the solver will ensure that operations are
88
+ completed before their respective deadlines.
75
89
 
76
90
  Returns:
77
91
  The best schedule found by the solver.
@@ -84,14 +98,30 @@ class ORToolsSolver(BaseSolver):
84
98
  """
85
99
  # Re-defined here since we already add metadata to the schedule in
86
100
  # the solve method.
87
- return self.solve(instance)
101
+ return self.solve(
102
+ instance, arrival_times=arrival_times, deadlines=deadlines
103
+ )
88
104
 
89
- def solve(self, instance: JobShopInstance) -> Schedule:
105
+ def solve(
106
+ self,
107
+ instance: JobShopInstance,
108
+ arrival_times: Sequence[Sequence[int]] | None = None,
109
+ deadlines: Sequence[Sequence[int]] | None = None,
110
+ ) -> Schedule:
90
111
  """Creates the variables, constraints and objective, and solves the
91
112
  problem.
92
113
 
93
114
  Args:
94
- instance: The job shop instance to be solved.
115
+ instance:
116
+ The job shop instance to be solved.
117
+ arrival_times:
118
+ Optional arrival times for each operation.
119
+ If provided, the solver will ensure that operations do not
120
+ start before their respective arrival times.
121
+ deadlines:
122
+ Optional deadlines for each operation.
123
+ If provided, the solver will ensure that operations are
124
+ completed before their respective deadlines.
95
125
 
96
126
  Returns:
97
127
  The best schedule found by the solver.
@@ -107,7 +137,9 @@ class ORToolsSolver(BaseSolver):
107
137
  If no solution could be found for the given problem within the
108
138
  time limit.
109
139
  """
110
- self._initialize_model(instance)
140
+ self._initialize_model(
141
+ instance, arrival_times=arrival_times, deadlines=deadlines
142
+ )
111
143
 
112
144
  start_time = time.perf_counter()
113
145
  status = self.solver.Solve(self.model)
@@ -128,7 +160,12 @@ class ORToolsSolver(BaseSolver):
128
160
  }
129
161
  return self._create_schedule(instance, metadata)
130
162
 
131
- def _initialize_model(self, instance: JobShopInstance):
163
+ def _initialize_model(
164
+ self,
165
+ instance: JobShopInstance,
166
+ arrival_times: Sequence[Sequence[int]] | None = None,
167
+ deadlines: Sequence[Sequence[int]] | None = None,
168
+ ):
132
169
  """Initializes the model with variables, constraints and objective.
133
170
 
134
171
  The model is initialized with two variables for each operation: start
@@ -147,7 +184,9 @@ class ORToolsSolver(BaseSolver):
147
184
  self.solver.parameters.max_time_in_seconds = (
148
185
  self.max_time_in_seconds
149
186
  )
150
- self._create_variables(instance)
187
+ self._create_variables(
188
+ instance, arrival_times=arrival_times, deadlines=deadlines
189
+ )
151
190
  self._add_constraints(instance)
152
191
  self._set_objective(instance)
153
192
 
@@ -177,15 +216,35 @@ class ORToolsSolver(BaseSolver):
177
216
  instance=instance, schedule=sorted_schedule, **metadata
178
217
  )
179
218
 
180
- def _create_variables(self, instance: JobShopInstance):
219
+ def _create_variables(
220
+ self,
221
+ instance: JobShopInstance,
222
+ arrival_times: Sequence[Sequence[int]] | None = None,
223
+ deadlines: Sequence[Sequence[int]] | None = None,
224
+ ):
181
225
  """Creates two variables for each operation: start and end time."""
182
226
  for job in instance.jobs:
183
227
  for operation in job:
228
+ # Initial Naive Bounds
229
+ lower_bound = 0
230
+ upper_bound = instance.total_duration
231
+
232
+ if arrival_times is not None:
233
+ lower_bound = arrival_times[operation.job_id][
234
+ operation.position_in_job
235
+ ]
236
+
237
+ if deadlines is not None:
238
+ op_deadline = deadlines[operation.job_id][
239
+ operation.position_in_job
240
+ ]
241
+ upper_bound = op_deadline
242
+
184
243
  start_var = self.model.NewIntVar(
185
- 0, instance.total_duration, f"start_{operation}"
244
+ lower_bound, upper_bound, f"start_{operation}"
186
245
  )
187
246
  end_var = self.model.NewIntVar(
188
- 0, instance.total_duration, f"end_{operation}"
247
+ lower_bound, upper_bound, f"end_{operation}"
189
248
  )
190
249
  self._operations_start[operation] = (start_var, end_var)
191
250
  self.model.Add(end_var == start_var + operation.duration)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 1.2.0
3
+ Version: 1.3.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
@@ -1,14 +1,14 @@
1
- job_shop_lib/__init__.py,sha256=yXJFP7NB9jntS26eZXQCVjeCM45KPFcc2FS1PbTCLm8,639
1
+ job_shop_lib/__init__.py,sha256=FHzL2Kx7_4FCgaaLxzb_RdTRDQgF-CraofLgc2IdSqs,639
2
2
  job_shop_lib/_base_solver.py,sha256=p17XmtufNc9Y481cqZUT45pEkUmmW1HWG53dfhIBJH8,1363
3
3
  job_shop_lib/_job_shop_instance.py,sha256=FkMBy9Yb8cNEGswI9vlN3Wh4mhtEX-QuDbKvSYUOXcM,18361
4
4
  job_shop_lib/_operation.py,sha256=lwCjgXwWlgESFuV3Yh4SCVofPGCd3hJU4vnK7peREac,4235
5
- job_shop_lib/_schedule.py,sha256=PX3wOv9Cw8NgjBLV3yDJW0mNl7a25nvoEV5Hdv7R_-g,11943
5
+ job_shop_lib/_schedule.py,sha256=3PgDZ-DZmlESh5TASNHTqW_8Z7XPVSF64knvXEGRIbM,12927
6
6
  job_shop_lib/_scheduled_operation.py,sha256=czrGr87EOTlO2NPolIN5CDigeiCzvQEyra5IZPwSFZc,2801
7
7
  job_shop_lib/benchmarking/__init__.py,sha256=JPnCw5mK7sADAW0HctVKHEDRw22afp9caNh2eUS36Ys,3290
8
8
  job_shop_lib/benchmarking/_load_benchmark.py,sha256=-cgyx0Kn6uAc3KdGFSQb6eUVQjQggmpVKOH9qusNkXI,2930
9
9
  job_shop_lib/benchmarking/benchmark_instances.json,sha256=F9EvyzFwVxiKAN6rQTsrMhsKstmyUmroyWduM7a00KQ,464841
10
10
  job_shop_lib/constraint_programming/__init__.py,sha256=kKQRUxxS_nVFUdXGnf4bQOD9mqrXxZZWElS753A4YiA,454
11
- job_shop_lib/constraint_programming/_ortools_solver.py,sha256=oMPeA2VHoYX1ZvmygQ8kYew40ITLAQATmM4OhgVFuXM,10482
11
+ job_shop_lib/constraint_programming/_ortools_solver.py,sha256=LMpfpgiU2etrtyVTKVKyZW1PVMrOG2TenWzfGuEGf2I,12710
12
12
  job_shop_lib/dispatching/__init__.py,sha256=gbgY1_lhergmXaDa-VYVUmxMpOKzYko0ONREVAt_QPc,2643
13
13
  job_shop_lib/dispatching/_dispatcher.py,sha256=KnV_Kry3Ie81WbKhdpRQtOMsuFDNCuh5Kp2ZnelM-R8,23835
14
14
  job_shop_lib/dispatching/_dispatcher_observer_config.py,sha256=QF2d3rJWwmvutQBAkKxzQ1toJs6eMelT404LGS2z9HQ,2467
@@ -67,7 +67,7 @@ job_shop_lib/visualization/gantt/_plot_gantt_chart.py,sha256=_4UGUTRuIw0tLzsJD9G
67
67
  job_shop_lib/visualization/graphs/__init__.py,sha256=HUWzfgQLeklNROtjnxeJX_FIySo_baTXO6klx0zUVpQ,630
68
68
  job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py,sha256=L9_ZGgvCFpGc2rTOdZESdtydFQqShjqedimIOhqZx6Y,16209
69
69
  job_shop_lib/visualization/graphs/_plot_resource_task_graph.py,sha256=nkkdZ-9_OBevw72Frecwzv1y3WyhGZ9r9lz0y9MXvZ8,13192
70
- job_shop_lib-1.2.0.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
71
- job_shop_lib-1.2.0.dist-info/METADATA,sha256=jn2o3HTpGzqzkYdagJfNM-IVheqnLQ6SEUnQQt69sYc,19130
72
- job_shop_lib-1.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
73
- job_shop_lib-1.2.0.dist-info/RECORD,,
70
+ job_shop_lib-1.3.0.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
71
+ job_shop_lib-1.3.0.dist-info/METADATA,sha256=oVgHOo9l8-crgD3w_uiR5zU6B0m9gxRAI0IPu7pSnpU,19130
72
+ job_shop_lib-1.3.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
73
+ job_shop_lib-1.3.0.dist-info/RECORD,,