job-shop-lib 0.1.3__py3-none-any.whl → 0.2.1__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/generators/transformations.py +164 -0
- job_shop_lib/graphs/job_shop_graph.py +10 -0
- {job_shop_lib-0.1.3.dist-info → job_shop_lib-0.2.1.dist-info}/METADATA +2 -4
- {job_shop_lib-0.1.3.dist-info → job_shop_lib-0.2.1.dist-info}/RECORD +6 -5
- {job_shop_lib-0.1.3.dist-info → job_shop_lib-0.2.1.dist-info}/LICENSE +0 -0
- {job_shop_lib-0.1.3.dist-info → job_shop_lib-0.2.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,164 @@
|
|
1
|
+
"""Classes for generating transformed JobShopInstance objects."""
|
2
|
+
|
3
|
+
import abc
|
4
|
+
import copy
|
5
|
+
import random
|
6
|
+
|
7
|
+
from job_shop_lib import JobShopInstance, Operation
|
8
|
+
|
9
|
+
|
10
|
+
class Transformation(abc.ABC):
|
11
|
+
"""Base class for transformations applied to JobShopInstance objects."""
|
12
|
+
|
13
|
+
def __init__(self, suffix: str = ""):
|
14
|
+
self.suffix = suffix
|
15
|
+
self.counter = 0
|
16
|
+
|
17
|
+
@abc.abstractmethod
|
18
|
+
def apply(self, instance: JobShopInstance) -> JobShopInstance:
|
19
|
+
"""Applies the transformation to a given JobShopInstance.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
instance: The JobShopInstance to transform.
|
23
|
+
|
24
|
+
Returns:
|
25
|
+
A new JobShopInstance with the transformation applied.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __call__(self, instance: JobShopInstance) -> JobShopInstance:
|
29
|
+
instance = self.apply(instance)
|
30
|
+
suffix = f"{self.suffix}_id={self.counter}"
|
31
|
+
instance.name += suffix
|
32
|
+
self.counter += 1
|
33
|
+
return instance
|
34
|
+
|
35
|
+
|
36
|
+
# pylint: disable=too-few-public-methods
|
37
|
+
class RemoveMachines(Transformation):
|
38
|
+
"""Removes operations associated with randomly selected machines until
|
39
|
+
there are exactly num_machines machines left."""
|
40
|
+
|
41
|
+
def __init__(self, num_machines: int, suffix: str | None = None):
|
42
|
+
if suffix is None:
|
43
|
+
suffix = f"_machines={num_machines}"
|
44
|
+
super().__init__(suffix=suffix)
|
45
|
+
self.num_machines = num_machines
|
46
|
+
|
47
|
+
def apply(self, instance: JobShopInstance) -> JobShopInstance:
|
48
|
+
if instance.num_machines <= self.num_machines:
|
49
|
+
return instance # No need to remove machines
|
50
|
+
|
51
|
+
# Select machine indices to keep
|
52
|
+
machines_to_keep = set(
|
53
|
+
random.sample(range(instance.num_machines), self.num_machines)
|
54
|
+
)
|
55
|
+
|
56
|
+
# Re-index machines
|
57
|
+
machine_reindex_map = {
|
58
|
+
old_id: new_id
|
59
|
+
for new_id, old_id in enumerate(sorted(machines_to_keep))
|
60
|
+
}
|
61
|
+
|
62
|
+
new_jobs = []
|
63
|
+
for job in instance.jobs:
|
64
|
+
# Keep operations whose machine_id is in machines_to_keep and
|
65
|
+
# re-index them
|
66
|
+
new_jobs.append(
|
67
|
+
[
|
68
|
+
Operation(machine_reindex_map[op.machine_id], op.duration)
|
69
|
+
for op in job
|
70
|
+
if op.machine_id in machines_to_keep
|
71
|
+
]
|
72
|
+
)
|
73
|
+
|
74
|
+
return JobShopInstance(new_jobs, instance.name)
|
75
|
+
|
76
|
+
|
77
|
+
# pylint: disable=too-few-public-methods
|
78
|
+
class AddDurationNoise(Transformation):
|
79
|
+
"""Adds uniform integer noise to operation durations."""
|
80
|
+
|
81
|
+
def __init__(
|
82
|
+
self,
|
83
|
+
min_duration: int = 1,
|
84
|
+
max_duration: int = 100,
|
85
|
+
noise_level: int = 10,
|
86
|
+
suffix: str | None = None,
|
87
|
+
):
|
88
|
+
if suffix is None:
|
89
|
+
suffix = f"_noise={noise_level}"
|
90
|
+
super().__init__(suffix=suffix)
|
91
|
+
self.min_duration = min_duration
|
92
|
+
self.max_duration = max_duration
|
93
|
+
self.noise_level = noise_level
|
94
|
+
|
95
|
+
def apply(self, instance: JobShopInstance) -> JobShopInstance:
|
96
|
+
new_jobs = []
|
97
|
+
for job in instance.jobs:
|
98
|
+
new_job = []
|
99
|
+
for op in job:
|
100
|
+
noise = random.randint(-self.noise_level, self.noise_level)
|
101
|
+
new_duration = max(
|
102
|
+
self.min_duration,
|
103
|
+
min(self.max_duration, op.duration + noise),
|
104
|
+
)
|
105
|
+
|
106
|
+
new_job.append(Operation(op.machine_id, new_duration))
|
107
|
+
new_jobs.append(new_job)
|
108
|
+
|
109
|
+
return JobShopInstance(new_jobs, instance.name)
|
110
|
+
|
111
|
+
|
112
|
+
class RemoveJobs(Transformation):
|
113
|
+
"""Removes jobs randomly until the number of jobs is within a specified
|
114
|
+
range."""
|
115
|
+
|
116
|
+
def __init__(
|
117
|
+
self,
|
118
|
+
min_jobs: int,
|
119
|
+
max_jobs: int,
|
120
|
+
target_jobs: int | None = None,
|
121
|
+
suffix: str | None = None,
|
122
|
+
):
|
123
|
+
"""
|
124
|
+
Args:
|
125
|
+
min_jobs: The minimum number of jobs to remain in the instance.
|
126
|
+
max_jobs: The maximum number of jobs to remain in the instance.
|
127
|
+
target_jobs: If specified, the number of jobs to remain in the
|
128
|
+
instance. Overrides min_jobs and max_jobs.
|
129
|
+
"""
|
130
|
+
if suffix is None:
|
131
|
+
suffix = f"_jobs={min_jobs}-{max_jobs}"
|
132
|
+
super().__init__(suffix=suffix)
|
133
|
+
self.min_jobs = min_jobs
|
134
|
+
self.max_jobs = max_jobs
|
135
|
+
self.target_jobs = target_jobs
|
136
|
+
|
137
|
+
def apply(self, instance: JobShopInstance) -> JobShopInstance:
|
138
|
+
if self.target_jobs is None:
|
139
|
+
target_jobs = random.randint(self.min_jobs, self.max_jobs)
|
140
|
+
else:
|
141
|
+
target_jobs = self.target_jobs
|
142
|
+
new_jobs = copy.deepcopy(instance.jobs)
|
143
|
+
|
144
|
+
while len(new_jobs) > target_jobs:
|
145
|
+
new_jobs.pop(random.randint(0, len(new_jobs) - 1))
|
146
|
+
|
147
|
+
return JobShopInstance(new_jobs, instance.name)
|
148
|
+
|
149
|
+
@staticmethod
|
150
|
+
def remove_job(
|
151
|
+
instance: JobShopInstance, job_index: int
|
152
|
+
) -> JobShopInstance:
|
153
|
+
"""Removes a specific job from the instance.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
instance: The JobShopInstance from which to remove the job.
|
157
|
+
job_index: The index of the job to remove.
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
A new JobShopInstance with the specified job removed.
|
161
|
+
"""
|
162
|
+
new_jobs = copy.deepcopy(instance.jobs)
|
163
|
+
new_jobs.pop(job_index)
|
164
|
+
return JobShopInstance(new_jobs, instance.name)
|
@@ -85,6 +85,16 @@ class JobShopGraph:
|
|
85
85
|
|
86
86
|
self._add_operation_nodes()
|
87
87
|
|
88
|
+
@property
|
89
|
+
def num_nodes(self) -> int:
|
90
|
+
"""Number of nodes in the graph."""
|
91
|
+
return len(self.nodes)
|
92
|
+
|
93
|
+
@property
|
94
|
+
def num_edges(self) -> int:
|
95
|
+
"""Number of edges in the graph."""
|
96
|
+
return self.graph.number_of_edges()
|
97
|
+
|
88
98
|
def _add_operation_nodes(self) -> None:
|
89
99
|
"""Adds operation nodes to the graph."""
|
90
100
|
for job in self.instance.jobs:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: job-shop-lib
|
3
|
-
Version: 0.1
|
3
|
+
Version: 0.2.1
|
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
|
@@ -33,8 +33,6 @@ Description-Content-Type: text/markdown
|
|
33
33
|
|
34
34
|
</div>
|
35
35
|
|
36
|
-
|
37
|
-
|
38
36
|
An easy-to-use and modular Python library for the Job Shop Scheduling Problem (JSSP) with a special focus on graph representations.
|
39
37
|
|
40
38
|
It provides intuitive data structures to represent instances and solutions, as well as solvers and visualization tools.
|
@@ -174,7 +172,7 @@ We can visualize the solution with a `DispatchingRuleSolver` as a gif:
|
|
174
172
|
|
175
173
|
```python
|
176
174
|
from job_shop_lib.visualization import create_gif, get_plot_function
|
177
|
-
from job_shop_lib.
|
175
|
+
from job_shop_lib.dispatching import DispatchingRuleSolver, DispatchingRule
|
178
176
|
|
179
177
|
plt.style.use("ggplot")
|
180
178
|
|
@@ -14,11 +14,12 @@ job_shop_lib/dispatching/pruning_functions.py,sha256=d94_uBHuESp4NSf_jBk1q8eBCfT
|
|
14
14
|
job_shop_lib/exceptions.py,sha256=0Wla1lK6E2u1o3t2hJj9hUwyoJ-1ebkXd42GdXFAhV0,899
|
15
15
|
job_shop_lib/generators/__init__.py,sha256=CrMExfhRbw_0TnYgJ1HwFmq13LEFYFU9wSFANmlSTSQ,154
|
16
16
|
job_shop_lib/generators/basic_generator.py,sha256=pbRDQWC2mnHU0dbc-T8wkdwVeJPlRn06nhFXuwKataQ,7533
|
17
|
+
job_shop_lib/generators/transformations.py,sha256=FI2qHrETATJUrQP3-RYhZAQ5boyEZ0CF2StDbacBej8,5290
|
17
18
|
job_shop_lib/graphs/__init__.py,sha256=mWyF0MypyYfvFhy2F93BJkFIVsxS_0ZqvPuc29B7TJg,1454
|
18
19
|
job_shop_lib/graphs/build_agent_task_graph.py,sha256=ktj-oNLUPmWHfL81EVyaoF4hXClWYfnN7oG2Nn4pOsg,7128
|
19
20
|
job_shop_lib/graphs/build_disjunctive_graph.py,sha256=IRMBtHw8aru5rYGz796-dc6QyaLJFh4LlPlN_BPSq5c,2877
|
20
21
|
job_shop_lib/graphs/constants.py,sha256=dqPF--okue5sF70Iv-YR14QKFx4pxPwT2dL1Rh5jylM,374
|
21
|
-
job_shop_lib/graphs/job_shop_graph.py,sha256=
|
22
|
+
job_shop_lib/graphs/job_shop_graph.py,sha256=2LDtZR1s61Lb-XjVE6SSu1Foca_YeMoCDVgT3XuNZKk,6229
|
22
23
|
job_shop_lib/graphs/node.py,sha256=wyKhSK6kUPWucPtbBv8E_BlFyFPg352u5fRBUnC6Mos,4880
|
23
24
|
job_shop_lib/job_shop_instance.py,sha256=ZB0NOcTvGSq0zmmxiDceaC0DH9ljpJXD0hfKOmP0jcE,12801
|
24
25
|
job_shop_lib/operation.py,sha256=dERsRpZLUwHMAPItd_KIHhbrKv0d1aS9GBabORktoEY,3862
|
@@ -29,7 +30,7 @@ job_shop_lib/visualization/agent_task_graph.py,sha256=G-c9eiawz6m9sdnDM1r-ZHz6K-
|
|
29
30
|
job_shop_lib/visualization/create_gif.py,sha256=3j339wjgGZKLOyMWGdVqVBQu4WFDUhyualHx8b3CJMQ,6382
|
30
31
|
job_shop_lib/visualization/disjunctive_graph.py,sha256=feiRAMxuG5CG2naO7I3HtcrSQw99yWxWzIGgZC_pxIs,5803
|
31
32
|
job_shop_lib/visualization/gantt_chart.py,sha256=OyBMBnjSsRC769qXimJ3IIQWlssgPfx-nlVeSeU5sWY,4415
|
32
|
-
job_shop_lib-0.1.
|
33
|
-
job_shop_lib-0.1.
|
34
|
-
job_shop_lib-0.1.
|
35
|
-
job_shop_lib-0.1.
|
33
|
+
job_shop_lib-0.2.1.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
|
34
|
+
job_shop_lib-0.2.1.dist-info/METADATA,sha256=DFmlsg1-gGkYYsqmUwMrNw_T31Vmu8WI_myPX2JSY6I,12624
|
35
|
+
job_shop_lib-0.2.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
36
|
+
job_shop_lib-0.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|