job-shop-lib 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,7 @@ from typing import Any
7
7
  from collections.abc import Callable
8
8
  from collections import deque
9
9
  from functools import wraps
10
+ from warnings import warn
10
11
 
11
12
  from job_shop_lib import (
12
13
  JobShopInstance,
@@ -155,21 +156,13 @@ class Dispatcher:
155
156
  def create_schedule_from_raw_solution(
156
157
  cls, instance: JobShopInstance, raw_solution: list[list[Operation]]
157
158
  ) -> Schedule:
158
- """Creates a schedule from a raw solution.
159
-
160
- A raw solution is a list of lists of operations, where each list
161
- represents the order of operations for a machine.
162
-
163
- Args:
164
- instance:
165
- The instance of the job shop problem to be solved.
166
- raw_solution:
167
- A list of lists of operations, where each list represents the
168
- order of operations for a machine.
169
-
170
- Returns:
171
- A Schedule object representing the solution.
172
- """
159
+ """Deprecated method, use `Schedule.from_job_sequences` instead."""
160
+ warn(
161
+ "Dispatcher.create_schedule_from_raw_solution is deprecated. "
162
+ "Use Schedule.from_job_sequences instead. It will be removed in "
163
+ "version 1.0.0.",
164
+ DeprecationWarning,
165
+ )
173
166
  dispatcher = cls(instance)
174
167
  dispatcher.reset()
175
168
  raw_solution_deques = [
job_shop_lib/operation.py CHANGED
@@ -107,10 +107,10 @@ class Operation:
107
107
  def __hash__(self) -> int:
108
108
  return hash(self.operation_id)
109
109
 
110
- def __eq__(self, __value: object) -> bool:
111
- if isinstance(__value, Operation):
112
- return self.operation_id == __value.operation_id
113
- return False
110
+ def __eq__(self, value: object) -> bool:
111
+ if not isinstance(value, Operation):
112
+ return False
113
+ return self.__slots__ == value.__slots__
114
114
 
115
115
  def __repr__(self) -> str:
116
116
  machines = (
job_shop_lib/schedule.py CHANGED
@@ -1,6 +1,11 @@
1
1
  """Home of the `Schedule` class."""
2
2
 
3
- from job_shop_lib import ScheduledOperation, JobShopInstance
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from collections import deque
7
+
8
+ from job_shop_lib import ScheduledOperation, JobShopInstance, JobShopLibError
4
9
 
5
10
 
6
11
  class Schedule:
@@ -71,6 +76,96 @@ class Schedule:
71
76
  """Returns the number of operations that have been scheduled."""
72
77
  return sum(len(machine_schedule) for machine_schedule in self.schedule)
73
78
 
79
+ def to_dict(self) -> dict:
80
+ """Returns a dictionary representation of the schedule.
81
+
82
+ This representation is useful for saving the instance to a JSON file.
83
+
84
+ Returns:
85
+ A dictionary representation of the schedule with the following
86
+ 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.
94
+ """
95
+ job_sequences: list[list[int]] = []
96
+ for machine_schedule in self.schedule:
97
+ job_sequences.append(
98
+ [operation.job_id for operation in machine_schedule]
99
+ )
100
+
101
+ return {
102
+ "instance": self.instance.to_dict(),
103
+ "job_sequences": job_sequences,
104
+ "metadata": self.metadata,
105
+ }
106
+
107
+ @staticmethod
108
+ def from_dict(
109
+ instance: dict[str, Any] | JobShopInstance,
110
+ job_sequences: list[list[int]],
111
+ metadata: dict[str, Any] | None = None,
112
+ ) -> Schedule:
113
+ """Creates a schedule from a dictionary representation."""
114
+ if isinstance(instance, dict):
115
+ instance = JobShopInstance.from_matrices(**instance)
116
+ schedule = Schedule.from_job_sequences(instance, job_sequences)
117
+ schedule.metadata = metadata if metadata is not None else {}
118
+ return schedule
119
+
120
+ @staticmethod
121
+ def from_job_sequences(
122
+ instance: JobShopInstance,
123
+ job_sequences: list[list[int]],
124
+ ) -> Schedule:
125
+ """Creates an active schedule from a list of job sequences.
126
+
127
+ An active schedule is the optimal schedule for the given job sequences.
128
+ In other words, it is not possible to construct another schedule,
129
+ through changes in the order of processing on the machines, with at
130
+ least one operation finishing earlier and no operation finishing later.
131
+
132
+ Args:
133
+ instance:
134
+ The `JobShopInstance` object that the schedule is for.
135
+ job_sequences:
136
+ A list of lists of job ids. Each list of job ids represents the
137
+ order of operations on the machine. The machine that the list
138
+ corresponds to is determined by the index of the list.
139
+
140
+ Returns:
141
+ A `Schedule` object with the given job sequences.
142
+ """
143
+ from job_shop_lib.dispatching import Dispatcher
144
+
145
+ dispatcher = Dispatcher(instance)
146
+ dispatcher.reset()
147
+ raw_solution_deques = [deque(job_ids) for job_ids in job_sequences]
148
+
149
+ while not dispatcher.schedule.is_complete():
150
+ at_least_one_operation_scheduled = False
151
+ for machine_id, job_ids in enumerate(raw_solution_deques):
152
+ if not job_ids:
153
+ continue
154
+ job_id = job_ids[0]
155
+ operation_index = dispatcher.job_next_operation_index[job_id]
156
+ operation = instance.jobs[job_id][operation_index]
157
+ is_ready = dispatcher.is_operation_ready(operation)
158
+ if is_ready and machine_id in operation.machines:
159
+ dispatcher.dispatch(operation, machine_id)
160
+ job_ids.popleft()
161
+ at_least_one_operation_scheduled = True
162
+
163
+ if not at_least_one_operation_scheduled:
164
+ raise JobShopLibError(
165
+ "Invalid job sequences. No valid operation to schedule."
166
+ )
167
+ return dispatcher.schedule
168
+
74
169
  def reset(self):
75
170
  """Resets the schedule to an empty state."""
76
171
  self.schedule = [[] for _ in range(self.instance.num_machines)]
@@ -1,6 +1,8 @@
1
1
  """Home of the `ScheduledOperation` class."""
2
2
 
3
- from job_shop_lib import Operation
3
+ from warnings import warn
4
+
5
+ from job_shop_lib import Operation, JobShopLibError
4
6
 
5
7
 
6
8
  class ScheduledOperation:
@@ -47,7 +49,7 @@ class ScheduledOperation:
47
49
  @machine_id.setter
48
50
  def machine_id(self, value: int):
49
51
  if value not in self.operation.machines:
50
- raise ValueError(
52
+ raise JobShopLibError(
51
53
  f"Operation cannot be scheduled on machine {value}. "
52
54
  f"Valid machines are {self.operation.machines}."
53
55
  )
@@ -62,18 +64,28 @@ class ScheduledOperation:
62
64
  """
63
65
 
64
66
  if self.operation.job_id is None:
65
- raise ValueError("Operation has no job_id.")
67
+ raise JobShopLibError("Operation has no job_id.")
66
68
  return self.operation.job_id
67
69
 
68
70
  @property
69
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
+ @property
81
+ def position_in_job(self) -> int:
70
82
  """Returns the position (starting at zero) of the operation in the job.
71
83
 
72
84
  Raises:
73
85
  ValueError: If the operation has no position_in_job.
74
86
  """
75
87
  if self.operation.position_in_job is None:
76
- raise ValueError("Operation has no position.")
88
+ raise JobShopLibError("Operation has no position.")
77
89
  return self.operation.position_in_job
78
90
 
79
91
  @property
@@ -91,7 +103,7 @@ class ScheduledOperation:
91
103
  if not isinstance(value, ScheduledOperation):
92
104
  return False
93
105
  return (
94
- self.operation is value.operation
106
+ self.operation == value.operation
95
107
  and self.start_time == value.start_time
96
108
  and self.machine_id == value.machine_id
97
109
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 0.3.0
3
+ Version: 0.4.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
@@ -6,7 +6,7 @@ job_shop_lib/benchmarking/load_benchmark.py,sha256=CjiSALutgWcfD-SDU6w9WO3udvPVp
6
6
  job_shop_lib/cp_sat/__init__.py,sha256=DqrF9IewFMkVB5BhFOHhlJvG6w6BW4ecxBXySunGLoU,97
7
7
  job_shop_lib/cp_sat/ortools_solver.py,sha256=zsISUQy0dQvn7bmUsAQBCe-V92CFskJHkSfngSP4KSg,8130
8
8
  job_shop_lib/dispatching/__init__.py,sha256=xk6NjndZ4-EH5G_fGSEX4LQEXL53TRYn5dKEb5uFggI,1568
9
- job_shop_lib/dispatching/dispatcher.py,sha256=O9iqytBveJT3WMTELkpNy8IeNiI2Yy8uxbP2ruLLT1A,12582
9
+ job_shop_lib/dispatching/dispatcher.py,sha256=2rzvmn6EQz2gIS8tP2tUPJ34uUq7SZzJubHmrjw_qV8,12394
10
10
  job_shop_lib/dispatching/dispatching_rule_solver.py,sha256=fbNfSclH6Jw1F-QGY1oxAj9wm2hHhJHGnsF2HateXX8,4669
11
11
  job_shop_lib/dispatching/dispatching_rules.py,sha256=SIDkPx_1uTkM0loEqGMqotLBBSaGi1gH0WS85GXrT_I,5557
12
12
  job_shop_lib/dispatching/factories.py,sha256=ldyIbz3QuLuDkrqbgJXV6YoM6AV6CKyHu8z4hXLG2Vo,7267
@@ -23,15 +23,15 @@ job_shop_lib/graphs/constants.py,sha256=dqPF--okue5sF70Iv-YR14QKFx4pxPwT2dL1Rh5j
23
23
  job_shop_lib/graphs/job_shop_graph.py,sha256=B0buqcg7US6UvIRWsoY8_FwqzPa_nVjnBu7hPIrygUo,7404
24
24
  job_shop_lib/graphs/node.py,sha256=FrSndtvqgRbN69jIcU6q1TkBh-LOGg8sxxYjDZqCcf4,5613
25
25
  job_shop_lib/job_shop_instance.py,sha256=ZB0NOcTvGSq0zmmxiDceaC0DH9ljpJXD0hfKOmP0jcE,12801
26
- job_shop_lib/operation.py,sha256=bZclBeyge71Avm9ArwvGuBKZp5Idw5EUm6m35jav0C4,3924
27
- job_shop_lib/schedule.py,sha256=1xzue2ro927VZw9IWg_tlBLZ7kDbN091aOW6ZMEjOYQ,6509
28
- job_shop_lib/scheduled_operation.py,sha256=EfG5AcrspjO3erhM2ejlSOtYKNnaNTsLEu2gu2N3FxA,3127
26
+ job_shop_lib/operation.py,sha256=S61x0xgu09JLwrRp7syd1P2psbl0ByGuK_hHoHp4ng8,3916
27
+ job_shop_lib/schedule.py,sha256=aODGwMv9slFIqOTCz2hF_EIpXhddz8-iAH5gSzGO5G8,10393
28
+ job_shop_lib/scheduled_operation.py,sha256=qzXzat1dQBbQ-sLyoG1iXbF9eWbdFeZDFjhAFVavHPk,3526
29
29
  job_shop_lib/visualization/__init__.py,sha256=Kxjk3ERYXPAHR72nkD92gFdJltSLA2kxLZrlZzZJS8o,693
30
30
  job_shop_lib/visualization/agent_task_graph.py,sha256=G-c9eiawz6m9sdnDM1r-ZHz6K-gYDIAreHpb6pkYE7w,8284
31
31
  job_shop_lib/visualization/create_gif.py,sha256=KrwMpSYvSCsL5Ld3taiNHSl_QDrODLpqM-MKQG_C2oU,6674
32
32
  job_shop_lib/visualization/disjunctive_graph.py,sha256=pg4KG9BfQbnBPnXYgbyPGe0AuHSmhYqPeqWYAf_spWQ,5905
33
33
  job_shop_lib/visualization/gantt_chart.py,sha256=OyBMBnjSsRC769qXimJ3IIQWlssgPfx-nlVeSeU5sWY,4415
34
- job_shop_lib-0.3.0.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
35
- job_shop_lib-0.3.0.dist-info/METADATA,sha256=DPjUe3anmq-s0y-_09o8uBjvFTmHqVt5N_AI3BPu1AM,12624
36
- job_shop_lib-0.3.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
37
- job_shop_lib-0.3.0.dist-info/RECORD,,
34
+ job_shop_lib-0.4.0.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
35
+ job_shop_lib-0.4.0.dist-info/METADATA,sha256=nWD2fekWRXglydO3tihH7tLHvYVhILm_TASf-Yn82qA,12624
36
+ job_shop_lib-0.4.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
37
+ job_shop_lib-0.4.0.dist-info/RECORD,,