job-shop-lib 0.1.3__py3-none-any.whl → 0.2.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 0.1.3
3
+ Version: 0.2.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
@@ -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.solvers import DispatchingRuleSolver, DispatchingRule
175
+ from job_shop_lib.dispatching import DispatchingRuleSolver, DispatchingRule
178
176
 
179
177
  plt.style.use("ggplot")
180
178
 
@@ -14,6 +14,7 @@ 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
@@ -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.3.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
33
- job_shop_lib-0.1.3.dist-info/METADATA,sha256=eHS0PpOAZqCUNW0-eWPOydr_l4CQ_VpkTWBJhN6be9w,12622
34
- job_shop_lib-0.1.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
35
- job_shop_lib-0.1.3.dist-info/RECORD,,
33
+ job_shop_lib-0.2.0.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
34
+ job_shop_lib-0.2.0.dist-info/METADATA,sha256=KWTHYD4dNAAzSmNRUoATnMKKvwtBudbMwcI4yAnCiqQ,12624
35
+ job_shop_lib-0.2.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
36
+ job_shop_lib-0.2.0.dist-info/RECORD,,