job-shop-lib 0.5.0__tar.gz → 0.5.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/PKG-INFO +1 -1
  2. job_shop_lib-0.5.1/job_shop_lib/generation/__init__.py +11 -0
  3. job_shop_lib-0.5.1/job_shop_lib/generation/general_instance_generator.py +169 -0
  4. job_shop_lib-0.5.1/job_shop_lib/generation/instance_generator.py +122 -0
  5. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/generators/__init__.py +2 -1
  6. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/generators/basic_generator.py +3 -0
  7. job_shop_lib-0.5.1/job_shop_lib/generators/transformations.py +164 -0
  8. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/pyproject.toml +1 -1
  9. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/LICENSE +0 -0
  10. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/README.md +0 -0
  11. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/__init__.py +0 -0
  12. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/base_solver.py +0 -0
  13. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/benchmarking/__init__.py +0 -0
  14. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
  15. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/benchmarking/load_benchmark.py +0 -0
  16. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/cp_sat/__init__.py +0 -0
  17. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/cp_sat/ortools_solver.py +0 -0
  18. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/__init__.py +0 -0
  19. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/dispatcher.py +0 -0
  20. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/dispatching_rule_solver.py +0 -0
  21. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/dispatching_rules.py +0 -0
  22. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/factories.py +0 -0
  23. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
  24. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/composite_feature_observer.py +0 -0
  25. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/duration_observer.py +0 -0
  26. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/earliest_start_time_observer.py +0 -0
  27. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/factory.py +0 -0
  28. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/feature_observer.py +0 -0
  29. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/is_completed_observer.py +0 -0
  30. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/is_ready_observer.py +0 -0
  31. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/is_scheduled_observer.py +0 -0
  32. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/position_in_job_observer.py +0 -0
  33. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/feature_observers/remaining_operations_observer.py +0 -0
  34. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/history_tracker.py +0 -0
  35. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/dispatching/pruning_functions.py +0 -0
  36. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/exceptions.py +0 -0
  37. {job_shop_lib-0.5.0/job_shop_lib/generators → job_shop_lib-0.5.1/job_shop_lib/generation}/transformations.py +0 -0
  38. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/graphs/__init__.py +0 -0
  39. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/graphs/build_agent_task_graph.py +0 -0
  40. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/graphs/build_disjunctive_graph.py +0 -0
  41. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/graphs/constants.py +0 -0
  42. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/graphs/job_shop_graph.py +0 -0
  43. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/graphs/node.py +0 -0
  44. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/job_shop_instance.py +0 -0
  45. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/operation.py +0 -0
  46. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/schedule.py +0 -0
  47. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/scheduled_operation.py +0 -0
  48. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/visualization/__init__.py +0 -0
  49. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/visualization/agent_task_graph.py +0 -0
  50. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/visualization/create_gif.py +0 -0
  51. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/visualization/disjunctive_graph.py +0 -0
  52. {job_shop_lib-0.5.0 → job_shop_lib-0.5.1}/job_shop_lib/visualization/gantt_chart.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 0.5.0
3
+ Version: 0.5.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
@@ -0,0 +1,11 @@
1
+ """Package for generating job shop instances."""
2
+
3
+ from job_shop_lib.generation.instance_generator import InstanceGenerator
4
+ from job_shop_lib.generation.general_instance_generator import (
5
+ GeneralInstanceGenerator,
6
+ )
7
+
8
+ __all__ = [
9
+ "InstanceGenerator",
10
+ "GeneralInstanceGenerator",
11
+ ]
@@ -0,0 +1,169 @@
1
+ """Home of the `BasicGenerator` class."""
2
+
3
+ import random
4
+
5
+ from job_shop_lib import JobShopInstance, Operation
6
+ from job_shop_lib.generation import InstanceGenerator
7
+
8
+
9
+ class GeneralInstanceGenerator(InstanceGenerator):
10
+ """Generates instances for job shop problems.
11
+
12
+ This class is designed to be versatile, enabling the creation of various
13
+ job shop instances without the need for multiple dedicated classes.
14
+
15
+ It supports customization of the number of jobs, machines, operation
16
+ durations, and more.
17
+
18
+ The class supports both single instance generation and iteration over
19
+ multiple instances, controlled by the `iteration_limit` parameter. It
20
+ implements the iterator protocol, allowing it to be used in a `for` loop.
21
+
22
+ Note:
23
+ When used as an iterator, the generator will produce instances until it
24
+ reaches the specified `iteration_limit`. If `iteration_limit` is None,
25
+ it will continue indefinitely.
26
+
27
+ Attributes:
28
+ num_jobs_range:
29
+ The range of the number of jobs to generate. If a single
30
+ int is provided, it is used as both the minimum and maximum.
31
+ duration_range:
32
+ The range of durations for each operation.
33
+ num_machines_range:
34
+ The range of the number of machines available. If a
35
+ single int is provided, it is used as both the minimum and maximum.
36
+ machines_per_operation:
37
+ Specifies how many machines each operation
38
+ can be assigned to. If a single int is provided, it is used for
39
+ all operations.
40
+ allow_less_jobs_than_machines:
41
+ If True, allows generating instances where the number of jobs is
42
+ less than the number of machines.
43
+ allow_recirculation:
44
+ If True, a job can visit the same machine more than once.
45
+ name_suffix:
46
+ A suffix to append to each instance's name for identification.
47
+ seed:
48
+ Seed for the random number generator to ensure reproducibility.
49
+ """
50
+
51
+ def __init__( # pylint: disable=too-many-arguments
52
+ self,
53
+ num_jobs: int | tuple[int, int] = (10, 20),
54
+ num_machines: int | tuple[int, int] = (5, 10),
55
+ duration_range: tuple[int, int] = (1, 99),
56
+ allow_less_jobs_than_machines: bool = True,
57
+ allow_recirculation: bool = False,
58
+ machines_per_operation: int | tuple[int, int] = 1,
59
+ name_suffix: str = "classic_generated_instance",
60
+ seed: int | None = None,
61
+ iteration_limit: int | None = None,
62
+ ):
63
+ """Initializes the instance generator with the given parameters.
64
+
65
+ Args:
66
+ num_jobs:
67
+ The range of the number of jobs to generate.
68
+ num_machines:
69
+ The range of the number of machines available.
70
+ duration_range:
71
+ The range of durations for each operation.
72
+ allow_less_jobs_than_machines:
73
+ Allows instances with fewer jobs than machines.
74
+ allow_recirculation:
75
+ Allows jobs to visit the same machine multiple times.
76
+ machines_per_operation:
77
+ Specifies how many machines each operation can be assigned to.
78
+ If a single int is provided, it is used for all operations.
79
+ name_suffix:
80
+ Suffix for instance names.
81
+ seed:
82
+ Seed for the random number generator.
83
+ iteration_limit:
84
+ Maximum number of instances to generate in iteration mode.
85
+ """
86
+ super().__init__(
87
+ num_jobs=num_jobs,
88
+ num_machines=num_machines,
89
+ duration_range=duration_range,
90
+ name_suffix=name_suffix,
91
+ seed=seed,
92
+ iteration_limit=iteration_limit,
93
+ )
94
+ if isinstance(machines_per_operation, int):
95
+ machines_per_operation = (
96
+ machines_per_operation,
97
+ machines_per_operation,
98
+ )
99
+ self.machines_per_operation = machines_per_operation
100
+
101
+ self.allow_less_jobs_than_machines = allow_less_jobs_than_machines
102
+ self.allow_recirculation = allow_recirculation
103
+ self.name_suffix = name_suffix
104
+
105
+ if seed is not None:
106
+ random.seed(seed)
107
+
108
+ def generate(self) -> JobShopInstance:
109
+ """Generates a single job shop instance"""
110
+ num_jobs = random.randint(*self.num_jobs_range)
111
+
112
+ min_num_machines, max_num_machines = self.num_machines_range
113
+ if not self.allow_less_jobs_than_machines:
114
+ min_num_machines = min(num_jobs, max_num_machines)
115
+ num_machines = random.randint(min_num_machines, max_num_machines)
116
+
117
+ jobs = []
118
+ available_machines = list(range(num_machines))
119
+ for _ in range(num_jobs):
120
+ job = []
121
+ for _ in range(num_machines):
122
+ operation = self.create_random_operation(available_machines)
123
+ job.append(operation)
124
+ jobs.append(job)
125
+ available_machines = list(range(num_machines))
126
+
127
+ return JobShopInstance(jobs=jobs, name=self._next_name())
128
+
129
+ def create_random_operation(
130
+ self, available_machines: list[int] | None = None
131
+ ) -> Operation:
132
+ """Creates a random operation with the given available machines.
133
+
134
+ Args:
135
+ available_machines:
136
+ A list of available machine_ids to choose from.
137
+ If None, all machines are available.
138
+ """
139
+ duration = random.randint(*self.duration_range)
140
+
141
+ if self.machines_per_operation[1] > 1:
142
+ machines = self._choose_multiple_machines()
143
+ return Operation(machines=machines, duration=duration)
144
+
145
+ machine_id = self._choose_one_machine(available_machines)
146
+ return Operation(machines=machine_id, duration=duration)
147
+
148
+ def _choose_multiple_machines(self) -> list[int]:
149
+ num_machines = random.randint(*self.machines_per_operation)
150
+ available_machines = list(range(num_machines))
151
+ machines = []
152
+ for _ in range(num_machines):
153
+ machine = random.choice(available_machines)
154
+ machines.append(machine)
155
+ available_machines.remove(machine)
156
+ return machines
157
+
158
+ def _choose_one_machine(
159
+ self, available_machines: list[int] | None = None
160
+ ) -> int:
161
+ if available_machines is None:
162
+ _, max_num_machines = self.num_machines_range
163
+ available_machines = list(range(max_num_machines))
164
+
165
+ machine_id = random.choice(available_machines)
166
+ if not self.allow_recirculation:
167
+ available_machines.remove(machine_id)
168
+
169
+ return machine_id
@@ -0,0 +1,122 @@
1
+ import abc
2
+
3
+ import random
4
+ from typing import Iterator
5
+
6
+ from job_shop_lib import JobShopInstance
7
+
8
+
9
+ class InstanceGenerator(abc.ABC):
10
+ """Common interface for all generators.
11
+
12
+ The class supports both single instance generation and iteration over
13
+ multiple instances, controlled by the `iteration_limit` parameter. It
14
+ implements the iterator protocol, allowing it to be used in a `for` loop.
15
+
16
+ Note:
17
+ When used as an iterator, the generator will produce instances until it
18
+ reaches the specified `iteration_limit`. If `iteration_limit` is None,
19
+ it will continue indefinitely.
20
+
21
+ Attributes:
22
+ num_jobs_range:
23
+ The range of the number of jobs to generate. If a single
24
+ int is provided, it is used as both the minimum and maximum.
25
+ duration_range:
26
+ The range of durations for each operation.
27
+ num_machines_range:
28
+ The range of the number of machines available. If a
29
+ single int is provided, it is used as both the minimum and maximum.
30
+ name_suffix:
31
+ A suffix to append to each instance's name for identification.
32
+ seed:
33
+ Seed for the random number generator to ensure reproducibility.
34
+ """
35
+
36
+ def __init__( # pylint: disable=too-many-arguments
37
+ self,
38
+ num_jobs: int | tuple[int, int] = (10, 20),
39
+ num_machines: int | tuple[int, int] = (5, 10),
40
+ duration_range: tuple[int, int] = (1, 99),
41
+ name_suffix: str = "generated_instance",
42
+ seed: int | None = None,
43
+ iteration_limit: int | None = None,
44
+ ):
45
+ """Initializes the instance generator with the given parameters.
46
+
47
+ Args:
48
+ num_jobs:
49
+ The range of the number of jobs to generate.
50
+ num_machines:
51
+ The range of the number of machines available.
52
+ duration_range:
53
+ The range of durations for each operation.
54
+ name_suffix:
55
+ Suffix for instance names.
56
+ seed:
57
+ Seed for the random number generator.
58
+ iteration_limit:
59
+ Maximum number of instances to generate in iteration mode.
60
+ """
61
+
62
+ if isinstance(num_jobs, int):
63
+ num_jobs = (num_jobs, num_jobs)
64
+ if isinstance(num_machines, int):
65
+ num_machines = (num_machines, num_machines)
66
+ if seed is not None:
67
+ random.seed(seed)
68
+
69
+ self.num_jobs_range = num_jobs
70
+ self.num_machines_range = num_machines
71
+ self.duration_range = duration_range
72
+ self.name_suffix = name_suffix
73
+
74
+ self._counter = 0
75
+ self._current_iteration = 0
76
+ self._iteration_limit = iteration_limit
77
+
78
+ @abc.abstractmethod
79
+ def generate(self) -> JobShopInstance:
80
+ """Generates a single job shop instance"""
81
+
82
+ def _next_name(self) -> str:
83
+ self._counter += 1
84
+ return f"{self.name_suffix}_{self._counter}"
85
+
86
+ def __iter__(self) -> Iterator[JobShopInstance]:
87
+ self._current_iteration = 0
88
+ return self
89
+
90
+ def __next__(self) -> JobShopInstance:
91
+ if (
92
+ self._iteration_limit is not None
93
+ and self._current_iteration >= self._iteration_limit
94
+ ):
95
+ raise StopIteration
96
+ self._current_iteration += 1
97
+ return self.generate()
98
+
99
+ def __len__(self) -> int:
100
+ if self._iteration_limit is None:
101
+ raise ValueError("Iteration limit is not set.")
102
+ return self._iteration_limit
103
+
104
+ @property
105
+ def max_num_jobs(self) -> int:
106
+ """Returns the maximum number of jobs that can be generated."""
107
+ return self.num_jobs_range[1]
108
+
109
+ @property
110
+ def min_num_jobs(self) -> int:
111
+ """Returns the minimum number of jobs that can be generated."""
112
+ return self.num_jobs_range[0]
113
+
114
+ @property
115
+ def max_num_machines(self) -> int:
116
+ """Returns the maximum number of machines that can be generated."""
117
+ return self.num_machines_range[1]
118
+
119
+ @property
120
+ def min_num_machines(self) -> int:
121
+ """Returns the minimum number of machines that can be generated."""
122
+ return self.num_machines_range[0]
@@ -1,4 +1,5 @@
1
- """Package for generating job shop instances."""
1
+ """DEPRECATED: Use `job_shop_lib.generation` instead. It will be removed in
2
+ version 1.0.0."""
2
3
 
3
4
  from job_shop_lib.generators.basic_generator import BasicGenerator
4
5
 
@@ -9,6 +9,9 @@ from job_shop_lib import JobShopInstance, Operation
9
9
  class BasicGenerator: # pylint: disable=too-many-instance-attributes
10
10
  """Generates instances for job shop problems.
11
11
 
12
+ DEPRECATED: Class moved to `job_shop_lib.generation` and renamed to
13
+ `GeneralInstanceGenerator`. This class will be removed in version 1.0.0.
14
+
12
15
  This class is designed to be versatile, enabling the creation of various
13
16
  job shop instances without the need for multiple dedicated classes.
14
17
 
@@ -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
  [tool.poetry]
2
2
  name = "job-shop-lib"
3
- version = "0.5.0"
3
+ version = "0.5.1"
4
4
  description = "An easy-to-use and modular Python library for the Job Shop Scheduling Problem (JSSP)"
5
5
  authors = ["Pabloo22 <pablete.arino@gmail.com>"]
6
6
  license = "MIT"
File without changes
File without changes