job-shop-lib 1.3.0__py3-none-any.whl → 1.6.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.
@@ -0,0 +1,182 @@
1
+ from collections.abc import Callable
2
+ import random
3
+
4
+ from job_shop_lib import Schedule, ScheduledOperation
5
+ from job_shop_lib.exceptions import ValidationError
6
+
7
+
8
+ NeighborGenerator = Callable[[Schedule, random.Random], Schedule]
9
+
10
+ _MAX_ATTEMPTS = 1000
11
+
12
+
13
+ def _swap_with_index_picker(
14
+ schedule: Schedule,
15
+ random_generator: random.Random | None,
16
+ index_picker: Callable[[list, random.Random], tuple[int, int]],
17
+ ) -> Schedule:
18
+ """Generates a neighbor schedule by swapping two positions chosen by a
19
+ strategy.
20
+
21
+ This private helper applies a swap on a randomly selected machine whose
22
+ sequence has at least two operations. The actual indices to swap are
23
+ chosen by the provided picker function. It attempts up to a fixed number
24
+ of times to produce a valid neighbor. If all attempts fail, it returns
25
+ the original schedule unchanged.
26
+
27
+ Args:
28
+ schedule:
29
+ Current schedule to perturb.
30
+
31
+ random_generator:
32
+ Source of randomness. If ``None``, a new generator is created.
33
+
34
+ index_picker:
35
+ Function that receives a machine sequence and a random generator
36
+ and returns two indices to swap.
37
+
38
+ Returns:
39
+ A valid neighbor schedule if a feasible swap is found, otherwise the
40
+ original schedule.
41
+ """
42
+ if random_generator is None:
43
+ random_generator = random.Random()
44
+ job_sequences = schedule.job_sequences()
45
+ valid_machines = [i for i, seq in enumerate(job_sequences) if len(seq) > 1]
46
+ if not valid_machines:
47
+ return schedule
48
+
49
+ for _ in range(_MAX_ATTEMPTS):
50
+ machine_id = random_generator.choice(valid_machines)
51
+ sequence = job_sequences[machine_id]
52
+ idx1, idx2 = index_picker(sequence, random_generator)
53
+ sequence[idx1], sequence[idx2] = sequence[idx2], sequence[idx1]
54
+ try:
55
+ return Schedule.from_job_sequences(
56
+ schedule.instance, job_sequences
57
+ )
58
+ except ValidationError:
59
+ pass
60
+ return schedule
61
+
62
+
63
+ def swap_adjacent_operations(
64
+ schedule: Schedule, random_generator: random.Random | None = None
65
+ ) -> Schedule:
66
+ """Generates a neighbor schedule by swapping two adjacent operations.
67
+
68
+ Selects a machine at random with at least two operations and swaps a pair
69
+ of adjacent operations in its sequence. Internally tries several times to
70
+ produce a valid neighbor; if none is found, the original schedule is
71
+ returned.
72
+
73
+ Args:
74
+ schedule:
75
+ Current schedule to perturb.
76
+
77
+ random_generator:
78
+ Source of randomness. If ``None``, a new generator is created.
79
+
80
+ Returns:
81
+ A valid neighbor schedule with one adjacent swap applied, or the
82
+ original schedule if no valid swap is found.
83
+ """
84
+
85
+ def adjacent_picker(seq: list, rng: random.Random) -> tuple[int, int]:
86
+ idx = rng.randint(0, len(seq) - 2)
87
+ return idx, idx + 1
88
+
89
+ return _swap_with_index_picker(schedule, random_generator, adjacent_picker)
90
+
91
+
92
+ def swap_random_operations(
93
+ schedule: Schedule, random_generator: random.Random | None = None
94
+ ) -> Schedule:
95
+ """Generates a neighbor schedule by swapping two random operations.
96
+
97
+ Selects a machine at random with at least two operations and swaps two
98
+ randomly chosen positions in its sequence. Internally tries several times
99
+ to produce a valid neighbor; if none is found, the original schedule is
100
+ returned.
101
+
102
+ Args:
103
+ schedule:
104
+ Current schedule to perturb.
105
+
106
+ random_generator:
107
+ Source of randomness. If ``None``, a new generator is created.
108
+
109
+ Returns:
110
+ A valid neighbor schedule with one random swap applied, or the
111
+ original schedule if no valid swap is found.
112
+ """
113
+
114
+ def random_picker(seq: list, rng: random.Random) -> tuple[int, int]:
115
+ idx1, idx2 = rng.sample(range(len(seq)), 2)
116
+ return idx1, idx2
117
+
118
+ return _swap_with_index_picker(schedule, random_generator, random_picker)
119
+
120
+
121
+ def swap_in_critical_path(
122
+ schedule: Schedule, random_generator: random.Random | None = None
123
+ ) -> Schedule:
124
+ """Generates a neighbor by targeting swaps on the critical path.
125
+
126
+ This operator focuses on pairs of consecutive scheduled operations along
127
+ the current critical path that share the same machine. Swapping such
128
+ operations directly perturbs the longest-duration chain of precedence
129
+ and resource constraints that determines the makespan.
130
+
131
+ Why target the critical path:
132
+
133
+ - The makespan is the length of the critical path; operations not on it
134
+ typically have slack, so reordering them often does not improve the
135
+ objective. By contrast, modifying machine order on the critical path
136
+ can shorten the longest path or unlock constraints that reduce
137
+ blocking and idle times.
138
+ - Swapping consecutive critical operations on the same machine always
139
+ results in a feasible schedule.
140
+
141
+ Behavior:
142
+
143
+ - Identifies all consecutive pairs on the critical path that run on the
144
+ same machine and swaps one of them at random.
145
+ - If no such pairs exist, it falls back to a standard adjacent swap.
146
+
147
+ Args:
148
+ schedule:
149
+ Current schedule to perturb.
150
+
151
+ random_generator:
152
+ Source of randomness. If ``None``, a new generator is created.
153
+
154
+ Returns:
155
+ A valid neighbor schedule that prioritizes swaps on the critical
156
+ path, or a neighbor produced by an adjacent swap fallback when none
157
+ applies.
158
+ """
159
+ if random_generator is None:
160
+ random_generator = random.Random()
161
+
162
+ critical_path = schedule.critical_path()
163
+ possible_swaps: list[tuple[ScheduledOperation, ScheduledOperation]] = []
164
+ for i, current_scheduled_op in enumerate(critical_path[:-1]):
165
+ next_scheduled_op = critical_path[i + 1]
166
+ if current_scheduled_op.machine_id == next_scheduled_op.machine_id:
167
+ possible_swaps.append((current_scheduled_op, next_scheduled_op))
168
+
169
+ if not possible_swaps:
170
+ return swap_adjacent_operations(schedule, random_generator)
171
+
172
+ op1, op2 = random_generator.choice(possible_swaps)
173
+ job_sequences = schedule.job_sequences()
174
+ machine_id = op1.machine_id
175
+ idx1 = job_sequences[machine_id].index(op1.operation.job_id)
176
+ idx2 = job_sequences[machine_id].index(op2.operation.job_id)
177
+
178
+ job_sequences[machine_id][idx1], job_sequences[machine_id][idx2] = (
179
+ job_sequences[machine_id][idx2],
180
+ job_sequences[machine_id][idx1],
181
+ )
182
+ return Schedule.from_job_sequences(schedule.instance, job_sequences)
@@ -0,0 +1,73 @@
1
+ from collections.abc import Callable
2
+ from job_shop_lib import Schedule
3
+
4
+
5
+ ObjectiveFunction = Callable[[Schedule], float]
6
+
7
+
8
+ def get_makespan_with_penalties_objective(
9
+ deadline_penalty_factor: float = 1_000_000,
10
+ due_date_penalty_factor: float = 100,
11
+ ) -> ObjectiveFunction:
12
+ """Builds an objective function that returns the makespan plus penalties.
13
+
14
+ This factory returns a callable that evaluates a Schedule as the sum of
15
+ its makespan and penalties for violating operation-level deadlines and due
16
+ dates.
17
+
18
+ Penalties are applied per scheduled operation that finishes after its
19
+ corresponding attribute value:
20
+
21
+ - Deadline violation: adds ``deadline_penalty_factor`` once per violating
22
+ operation (hard constraint surrogate).
23
+ - Due date violation: adds ``due_date_penalty_factor`` once per violating
24
+ operation (soft constraint surrogate).
25
+
26
+ Args:
27
+ deadline_penalty_factor:
28
+ Cost added for each operation that
29
+ finishes after its deadline. Defaults to 1_000_000.
30
+ due_date_penalty_factor:
31
+ Cost added for each operation that
32
+ finishes after its due date. Defaults to 100.
33
+
34
+ Returns:
35
+ A function ``f(schedule) -> float`` that
36
+ computes ``schedule.makespan() + penalty``.
37
+
38
+ Notes:
39
+ - Deadlines and due dates are taken from each operation. If an
40
+ operation does not define the attribute (``None``), no penalty is
41
+ applied for that attribute.
42
+ - If the instance has neither deadlines nor due dates, the objective is
43
+ simply the makespan.
44
+ """
45
+
46
+ def objective(schedule: Schedule) -> float:
47
+ makespan = schedule.makespan()
48
+ instance = schedule.instance
49
+
50
+ # Fast path: no constraint attributes present in the instance
51
+ if not instance.has_deadlines and not instance.has_due_dates:
52
+ return makespan
53
+
54
+ penalty = 0.0
55
+ for machine_schedule in schedule.schedule:
56
+ for scheduled_op in machine_schedule:
57
+ op = scheduled_op.operation
58
+ # Deadline (hard) penalty
59
+ if (
60
+ op.deadline is not None
61
+ and scheduled_op.end_time > op.deadline
62
+ ):
63
+ penalty += deadline_penalty_factor
64
+ # Due date (soft) penalty
65
+ if (
66
+ op.due_date is not None
67
+ and scheduled_op.end_time > op.due_date
68
+ ):
69
+ penalty += due_date_penalty_factor
70
+
71
+ return makespan + penalty
72
+
73
+ return objective
@@ -0,0 +1,163 @@
1
+ from job_shop_lib import BaseSolver, JobShopInstance, Schedule
2
+ from job_shop_lib.metaheuristics import JobShopAnnealer
3
+ from job_shop_lib.dispatching.rules import DispatchingRuleSolver
4
+ from job_shop_lib.metaheuristics import (
5
+ NeighborGenerator,
6
+ swap_in_critical_path,
7
+ ObjectiveFunction,
8
+ )
9
+
10
+
11
+ class SimulatedAnnealingSolver(BaseSolver):
12
+ """Wraps the :class:`JobShopAnnealer` to follow the
13
+ :class`~job_shop_lib.BaseSolver` interface.
14
+
15
+ .. seealso::
16
+ See the documentation of the :class:`JobShopAnnealer` class for more
17
+ details on the annealing process.
18
+
19
+ Attributes:
20
+ initial_temperature:
21
+ Initial temperature for the annealing process. It controls the
22
+ probability of accepting worse solutions. That sets the metropolis
23
+ criterion. Corresponds to the `tmax` parameter in the annealer.
24
+ ending_temperature:
25
+ Ending temperature for the annealing process. It controls when to
26
+ stop accepting worse solutions. Corresponds to the `tmin` parameter
27
+ in the annealer.
28
+ steps:
29
+ Number of steps to perform in the annealing process. This is the
30
+ number of iterations the algorithm will run.
31
+ updates:
32
+ The number of progress updates to print during the annealing
33
+ process. Set to 0 to disable updates.
34
+ seed:
35
+ Random seed for reproducibility. If ``None``, random behavior will
36
+ be non-deterministic.
37
+
38
+ Args:
39
+ initial_temperature:
40
+ Initial temperature for the annealing process. It controls the
41
+ probability of accepting worse solutions. That sets the metropolis
42
+ criterion. Corresponds to the `tmax` parameter in the annealer.
43
+ ending_temperature:
44
+ Ending temperature for the annealing process. It controls when to
45
+ stop accepting worse solutions. Corresponds to the `tmin` parameter
46
+ in the annealer.
47
+ steps:
48
+ Number of steps to perform in the annealing process. This is the
49
+ number of iterations the algorithm will run.
50
+ updates:
51
+ The number of progress updates to print during the annealing
52
+ process. Set to 0 to disable updates.
53
+ seed:
54
+ Random seed for reproducibility. If ``None``, random behavior will
55
+ be non-deterministic.
56
+ """
57
+
58
+ def __init__(
59
+ self,
60
+ *,
61
+ initial_temperature: float = 25000,
62
+ ending_temperature: float = 2.5,
63
+ steps: int = 50_000,
64
+ updates: int = 100,
65
+ objective_function: ObjectiveFunction | None = None,
66
+ seed: int | None = None,
67
+ neighbor_generator: NeighborGenerator = swap_in_critical_path,
68
+ ):
69
+ self.initial_temperature = initial_temperature
70
+ self.ending_temperature = ending_temperature
71
+ self.steps = steps
72
+ self.updates = updates
73
+ self.objective_function = objective_function
74
+ self.seed = seed
75
+ self.neighbor_generator = neighbor_generator
76
+ self.annealer_: JobShopAnnealer | None = None
77
+
78
+ def setup_annealer(
79
+ self, instance: JobShopInstance, initial_state: Schedule | None = None
80
+ ) -> None:
81
+ """Initializes the annealer with the given instance and initial state.
82
+
83
+ Args:
84
+ instance:
85
+ The job shop instance to solve.
86
+ initial_state:
87
+ Initial state of the schedule as a list of lists, where each
88
+ sublist represents the operations of a job.
89
+ """
90
+ if initial_state is None:
91
+ initial_state = self._generate_initial_state(instance)
92
+
93
+ annealer = JobShopAnnealer(
94
+ instance,
95
+ initial_state,
96
+ objective_function=self.objective_function,
97
+ seed=self.seed,
98
+ neighbor_generator=self.neighbor_generator,
99
+ )
100
+ best_hparams = {
101
+ "tmax": self.initial_temperature,
102
+ "tmin": self.ending_temperature,
103
+ "steps": self.steps,
104
+ "updates": self.updates,
105
+ }
106
+ annealer.set_schedule(best_hparams)
107
+ self.annealer_ = annealer
108
+
109
+ def solve(
110
+ self,
111
+ instance: JobShopInstance,
112
+ initial_state: Schedule | None = None,
113
+ ) -> Schedule:
114
+ """Solves the given Job Shop Scheduling problem using
115
+ simulated annealing.
116
+
117
+ Args:
118
+ instance:
119
+ The job shop problem instance to solve.
120
+ initial_state:
121
+ Initial job sequences for each machine. A job sequence is a
122
+ list of job ids. Each list of job ids represents the order of
123
+ operations on the machine. The machine that the list
124
+ corresponds to is determined by the index of the list. If
125
+ ``None``, the solver will generate an initial state using the
126
+ :class:`DispatchingRuleSolver`.
127
+
128
+ Returns:
129
+ The best schedule found.
130
+
131
+ """
132
+ self.setup_annealer(instance, initial_state)
133
+ # For type checking purposes, we assert that the annealer is set up.
134
+ assert (
135
+ self.annealer_ is not None
136
+ ), "There was a problem setting up the annealer."
137
+ try:
138
+ best_state, _ = self.annealer_.anneal()
139
+ except KeyboardInterrupt:
140
+ # If the annealing process is interrupted, we return the best state
141
+ # found so far.
142
+ best_state = self.annealer_.best_state
143
+ return best_state
144
+
145
+ @staticmethod
146
+ def _generate_initial_state(instance: JobShopInstance) -> Schedule:
147
+ """Uses the
148
+ :class:`~job_shop_lib.dispatching.rules.DispatchingRuleSolver` to
149
+ generate an initial state for the annealer.
150
+
151
+ .. note::
152
+ The first solution might be unfeasible if the job shop instance
153
+ has deadlines.
154
+
155
+ Args:
156
+ instance (JobShopInstance): The job shop problem instance.
157
+
158
+ Returns:
159
+ An initial schedule generated by the dispatching rule solver.
160
+ """
161
+ solver = DispatchingRuleSolver()
162
+ schedule = solver.solve(instance)
163
+ return schedule
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 1.3.0
3
+ Version: 1.6.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
@@ -21,6 +21,7 @@ Requires-Dist: ortools (>=9.9,<10.0) ; sys_platform != "darwin"
21
21
  Requires-Dist: ortools (>=9.9,<9.13) ; sys_platform == "darwin"
22
22
  Requires-Dist: pyarrow (>=15,<21)
23
23
  Requires-Dist: pygraphviz (>=1.12,<2.0) ; extra == "pygraphviz"
24
+ Requires-Dist: simanneal (>=0.5.0,<0.6.0)
24
25
  Description-Content-Type: text/markdown
25
26
 
26
27
  <div align="center">
@@ -42,49 +43,34 @@ JobShopLib is a Python package for creating, solving, and visualizing job shop s
42
43
 
43
44
  It follows a modular design, allowing users to easily extend the library with new solvers, dispatching rules, visualization functions, etc.
44
45
 
45
- There is a [documentation page](https://job-shop-lib.readthedocs.io/en/stable/) for versions 1.0.0a3 and onward. See the [latest pull requests](https://github.com/Pabloo22/job_shop_lib/pulls?q=is%3Apr+is%3Aclosed) for the latest changes.
46
+ We support multiple solvers, including:
47
+ - **Constraint Programming**: Based on OR-Tools' CP-SAT solver. It supports **release dates, deadlines, and due dates.** See the ["Solving the Problem" tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/02-Solving-the-Problem.ipynb) for an example.
48
+ - **Dispatching Rules**: A set of predefined rules and the ability to create custom ones. They support arbitrary **setup times, machine breakdowns, release dates, deadlines, and due dates**. See the [following example](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/03-Dispatching-Rules.ipynb). You can also create videos or GIFs of the scheduling process. For creating GIFs or videos, see the [Save Gif example](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/04-Save-Gif.ipynb).
49
+ - **Metaheuristics**: Currently, we have a **simulated annealing** implementation that supports **release dates, deadlines, and due dates**. We also support arbitrary neighborhood search strategies, including swapping operations in the critical path as described in the paper "Job Shop Scheduling by Simulated Annealing" by van Laarhoven et al. (1992); and energy functions. See our [simulated annealing tutorial](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/03-Simulated-Annealing.ipynb).
50
+ - **Reinforcement Learning**: Two Gymnasium environments for solving the problem with **graph neural networks** (GNNs) or any other method. The environments support **setup times, release dates, deadlines, and due dates.** We're currently building a tutorial on how to use them.
46
51
 
47
- See [`gnn_scheduler`](https://github.com/Pabloo22/gnn_scheduler/blob/main/gnn_scheduler/) for an example implementation of a graph neural network-based dispatcher trained with [PyTorch Geometric](https://pyg.org/).
48
-
49
- See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide! More advanced examples can be found [here](https://job-shop-lib.readthedocs.io/en/stable/examples.html).
52
+ We also provide useful utilities, data structures, and visualization functions:
53
+ - **Intuitive Data Structures**: Easily create, manage, and manipulate job shop instances and solutions with user-friendly data structures. See [Getting Started](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/00-Getting-Started.ipynb) and [How Solutions are Represented](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/01-How-Solutions-are-Represented.ipynb).
54
+ - **Benchmark Instances**: Load well-known benchmark instances directly from the library without manual downloading. See [Load Benchmark Instances](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/05-Load-Benchmark-Instances.ipynb).
55
+ - **Random Instance Generation**: Create random instances with customizable sizes and properties. See [`generation`](https://job-shop-lib.readthedocs.io/en/stable/api/job_shop_lib.generation.html#module-job_shop_lib.generation) module.
56
+ - **Gantt Charts**: Visualize final schedules and how they are created iteratively by dispatching rule solvers or sequences of scheduling decisions with GIFs or videos.
57
+ - **Graph Representations**: Represent and visualize instances as disjunctive graphs or agent-task graphs (introduced in the ScheduleNet paper). Build your own custom graphs with the `JobShopGraph` class. See the [Disjunctive Graphs](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/04-Disjunctive-Graphs.ipynb) and [Resource Task Graphs](https://job-shop-lib.readthedocs.io/en/stable/examples/07-Resource-Task-Graph.html) examples.
50
58
 
51
59
  ## Installation :package:
52
60
 
53
61
  <!-- start installation -->
54
62
 
55
- JobShopLib is distributed on [PyPI](https://pypi.org/project/job-shop-lib/). You can install the latest stable version using `pip`:
56
-
57
63
  ```bash
58
64
  pip install job-shop-lib
59
65
  ```
60
66
 
61
- <!-- end installation -->
62
-
63
- <!-- key features -->
64
-
65
- ## Key Features :star:
66
-
67
- - **Data Structures**: Easily create, manage, and manipulate job shop instances and solutions with user-friendly data structures. See [Getting Started](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/00-Getting-Started.ipynb) and [How Solutions are Represented](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/01-How-Solutions-are-Represented.ipynb).
68
-
69
- - **Benchmark Instances**: Load well-known benchmark instances directly from the library without manual downloading. See [Load Benchmark Instances](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/05-Load-Benchmark-Instances.ipynb).
70
-
71
- - **Random Instance Generation**: Create random instances with customizable sizes and properties. See [`generation`](job_shop_lib/generation) package.
72
-
73
- - **Multiple Solvers**:
74
- - **Constraint Programming Solver**: OR-Tools' CP-SAT solver. See [Solving the Problem](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/tutorial/02-Solving-the-Problem.ipynb).
75
-
76
- - **Dispatching Rule Solvers**: Use any of the available dispatching rules or create custom ones. See [Dispatching Rules](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/03-Dispatching-Rules.ipynb).
77
-
78
- - **Gantt Charts**: Visualize final schedules and how are they created iteratively by dispatching rule solvers or sequences of scheduling decisions with GIFs or videos. See [Save Gif](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/06-Save-Gif.ipynb).
79
-
80
- - **Graph Representations**:
81
- - **Disjunctive Graphs**: Represent and visualize instances as disjunctive graphs. See [Disjunctive Graph](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/04-Disjunctive-Graph.ipynb).
82
- - **Agent-Task Graphs**: Encode instances as agent-task graphs (introduced in [ScheduleNet paper](https://arxiv.org/abs/2106.03051)). See [Agent-Task Graph](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/07-Agent-Task-Graph.ipynb).
83
- - Build your own custom graphs with the `JobShopGraph` class.
67
+ or
84
68
 
85
- - **Gymnasium Environments**: Two environments for solving the problem with graph neural networks (GNNs) or any other method, and reinforcement learning (RL). See [SingleJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/09-SingleJobShopGraphEnv.ipynb) and [MultiJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/10-MultiJobShopGraphEnv.ipynb).
69
+ ```bash
70
+ poetry add job-shop-lib
71
+ ```
86
72
 
87
- <!-- end key features -->
73
+ <!-- end installation -->
88
74
 
89
75
  ## Publication :scroll:
90
76
 
@@ -219,6 +205,7 @@ A dispatching rule is a heuristic guideline used to prioritize and sequence jobs
219
205
  ```python
220
206
  class DispatchingRule(str, Enum):
221
207
  SHORTEST_PROCESSING_TIME = "shortest_processing_time"
208
+ LARGEST_PROCESSING_TIME = "largest_processing_time"
222
209
  FIRST_COME_FIRST_SERVED = "first_come_first_served"
223
210
  MOST_WORK_REMAINING = "most_work_remaining"
224
211
  MOST_OPERATION_REMAINING = "most_operation_remaining"
@@ -432,6 +419,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
432
419
 
433
420
  ## References :books:
434
421
 
422
+ - Peter J. M. van Laarhoven, Emile H. L. Aarts, Jan Karel Lenstra, (1992) Job Shop Scheduling by Simulated Annealing. Operations Research 40(1):113-125.
423
+
435
424
  - J. Adams, E. Balas, and D. Zawack, "The shifting bottleneck procedure
436
425
  for job shop scheduling," Management Science, vol. 34, no. 3,
437
426
  pp. 391–401, 1988.
@@ -1,26 +1,27 @@
1
- job_shop_lib/__init__.py,sha256=FHzL2Kx7_4FCgaaLxzb_RdTRDQgF-CraofLgc2IdSqs,639
2
- job_shop_lib/_base_solver.py,sha256=p17XmtufNc9Y481cqZUT45pEkUmmW1HWG53dfhIBJH8,1363
3
- job_shop_lib/_job_shop_instance.py,sha256=FkMBy9Yb8cNEGswI9vlN3Wh4mhtEX-QuDbKvSYUOXcM,18361
4
- job_shop_lib/_operation.py,sha256=lwCjgXwWlgESFuV3Yh4SCVofPGCd3hJU4vnK7peREac,4235
5
- job_shop_lib/_schedule.py,sha256=3PgDZ-DZmlESh5TASNHTqW_8Z7XPVSF64knvXEGRIbM,12927
1
+ job_shop_lib/__init__.py,sha256=7AD9gwI3xXZonyTg82RJ8dhTXg1QdRJpL3HA0Y7TElg,639
2
+ job_shop_lib/_base_solver.py,sha256=8CCSiA2-DegCKRXhMw7yYyI8iPauTSuLku2LQ8dU-9U,1382
3
+ job_shop_lib/_job_shop_instance.py,sha256=_92orxdi70645J7cQlRE1I0PebvpHRCP6958q9j2h18,24261
4
+ job_shop_lib/_operation.py,sha256=JI5WjvRXNBeSpPOv3ZwSrUJ4jsVDJYKfMaDHYOaFYts,5945
5
+ job_shop_lib/_schedule.py,sha256=u0clgm2OTUsTstwd_AQeGQWANhmbWRrPPsKAc62iel4,17783
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=LMpfpgiU2etrtyVTKVKyZW1PVMrOG2TenWzfGuEGf2I,12710
11
+ job_shop_lib/constraint_programming/_ortools_solver.py,sha256=trTQtqSL2F2PXxd9RPnFhxaY8blNcfFUhTdab5QP9VU,12585
12
12
  job_shop_lib/dispatching/__init__.py,sha256=gbgY1_lhergmXaDa-VYVUmxMpOKzYko0ONREVAt_QPc,2643
13
- job_shop_lib/dispatching/_dispatcher.py,sha256=KnV_Kry3Ie81WbKhdpRQtOMsuFDNCuh5Kp2ZnelM-R8,23835
13
+ job_shop_lib/dispatching/_dispatcher.py,sha256=A54Q3_hm9Qy5Vfzeudj2QFxd9SWo9lqqBN55S6z-Who,23976
14
14
  job_shop_lib/dispatching/_dispatcher_observer_config.py,sha256=QF2d3rJWwmvutQBAkKxzQ1toJs6eMelT404LGS2z9HQ,2467
15
15
  job_shop_lib/dispatching/_factories.py,sha256=j3MhIwVXiq-B8JMit72ObvXSa2sdgWNhUD86gghL6Gg,4689
16
16
  job_shop_lib/dispatching/_history_observer.py,sha256=Vl8rQaxekUeEB-AyNxyC3c76zQakeh-rdri2iDnZvXw,610
17
17
  job_shop_lib/dispatching/_optimal_operations_observer.py,sha256=2EYxevjpeGMP3do-m0ZmtmjIjmNcxrWOSKzN_bW37gQ,4247
18
- job_shop_lib/dispatching/_ready_operation_filters.py,sha256=brhmhoyyoZ98wAEEfneZC-CD-aw9SerZHGMB1DpK8HY,5749
18
+ job_shop_lib/dispatching/_ready_operation_filters.py,sha256=DcwPDIF1eqVR6s3DV4J3cumii0N_apC13XA-50wjlI0,5822
19
19
  job_shop_lib/dispatching/_start_time_calculators.py,sha256=N4kz3c4TmXbyFsY6ctxruYK2ucnjSVXWNMhvsUWFuDg,8192
20
20
  job_shop_lib/dispatching/_unscheduled_operations_observer.py,sha256=0he-j4OlvqtXAJZD5x1nuBnUKqZUfftVx9NT3CVxPyg,2708
21
- job_shop_lib/dispatching/feature_observers/__init__.py,sha256=EuJLvSpJpoXUK8A4UuC2k6Mpa293ZR3oCnnvYivIBtU,2240
21
+ job_shop_lib/dispatching/feature_observers/__init__.py,sha256=Pzud4tuO_t72d9KY_nEH-stGOvKUTNjo_6GeWDuJPvc,2322
22
22
  job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py,sha256=tpvqTLIcNmbYROSFT62LiUZ_tI4fHWL_qCULKK43BU4,6429
23
- job_shop_lib/dispatching/feature_observers/_duration_observer.py,sha256=fbkUIVScF1iNjdVCYr1ImQm53TfahvVnGXhsRAsgdzY,4129
23
+ job_shop_lib/dispatching/feature_observers/_dates_observer.py,sha256=oCk1XAo_2mrgD0ckHQLw3dD7DSQVVg7xBKn7D_u1Dvc,6083
24
+ job_shop_lib/dispatching/feature_observers/_duration_observer.py,sha256=pBsJjT-1pbSi32hoLppoqXCftBvJPSh7r7tl3m7etAQ,4225
24
25
  job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py,sha256=AQIjVp7VRDnb5GuYZlLUwk-xiXSqbsxJW-Ji7NjLoAw,11452
25
26
  job_shop_lib/dispatching/feature_observers/_factory.py,sha256=NyXYK5A1hXsYEeEqngwVRNAFkevY95DglheeqyfFv8s,3217
26
27
  job_shop_lib/dispatching/feature_observers/_feature_observer.py,sha256=qbgtMUicQ5FWS-Ql4Izjsj4QrevfOGlWzoJ0JlVSLH0,8668
@@ -29,17 +30,17 @@ job_shop_lib/dispatching/feature_observers/_is_ready_observer.py,sha256=wy_pA-1w
29
30
  job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py,sha256=OcuMUB9_By6ZMtX-1_3z-xaxGbP85a5Zv0ywAv7XxWQ,1491
30
31
  job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py,sha256=WRknpQBKXs6h6cXLFJW7ZCvjtU8CPL-iXXNPw3g-mLE,1303
31
32
  job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py,sha256=5V87lCrJUabEe8AkTGXPu5yS8OGxeN8L3-xNyHmdmLs,1441
32
- job_shop_lib/dispatching/rules/__init__.py,sha256=0Nn9FBVmxVYeDeLsd7g7WkmKFBYJqOIDzArbqsC7FAI,2187
33
- job_shop_lib/dispatching/rules/_dispatching_rule_factory.py,sha256=5fNpv90fAoR6rcE6NeJOWiB7ir-FVnoONIhHtKJ9H0E,2904
33
+ job_shop_lib/dispatching/rules/__init__.py,sha256=u1XCRU4tVXJ2rdxXQabPIBaRly0PbBmBu1QcEKx9Z_Y,2396
34
+ job_shop_lib/dispatching/rules/_dispatching_rule_factory.py,sha256=iwqGHtroZAuE90fKOAe79reHlJ8gCnJzMjCkI0lucMg,3101
34
35
  job_shop_lib/dispatching/rules/_dispatching_rule_solver.py,sha256=1_canC1lXZATrQCZaHOY3JOLmTuT6U0Z_QWzgTOLwqI,5917
35
- job_shop_lib/dispatching/rules/_dispatching_rules_functions.py,sha256=Yk40aKePBHHiMO6aTFeyJd1-khsDPhqit2WCOaByCfw,10998
36
+ job_shop_lib/dispatching/rules/_dispatching_rules_functions.py,sha256=c-T6jUXZ2wjkmENBlDfNyWUA4T3inGll1E0jqAn6wd8,12052
36
37
  job_shop_lib/dispatching/rules/_machine_chooser_factory.py,sha256=CJ74ujgWXgG8cuULWY6VJkD_b3arTcOjTNLZJTAf8xE,2346
37
38
  job_shop_lib/dispatching/rules/_utils.py,sha256=m5qw4qyfaIvVrkmv51nuhreizr98-cg8AJKt2VTd48w,4603
38
39
  job_shop_lib/exceptions.py,sha256=ARzpoZJCvRIvOesCiqqFSRxkv6w9WwEXx0aBP-l2IKA,1597
39
40
  job_shop_lib/generation/__init__.py,sha256=QaWwuBfBNnOiG0OPiP_CV_flBu9dX7r2o_HwL47tREM,822
40
41
  job_shop_lib/generation/_general_instance_generator.py,sha256=b_tnyP4H_buoN7b6lKQRLvDkeZDdys0mpqS3thB5-SQ,6544
41
42
  job_shop_lib/generation/_instance_generator.py,sha256=doN6WySyI0k7wz3aKy_e6hj6t7WV3dNzve3YmTFShas,4584
42
- job_shop_lib/generation/_utils.py,sha256=TYBGt4Zjw94l6ukIjXBVAK3lmrrZXdyzyq_r1DMlL-E,3986
43
+ job_shop_lib/generation/_utils.py,sha256=b3SVU5DY3-VHXX2yrOwM7ABDSexiSFSRbo1d5QjRfoI,3972
43
44
  job_shop_lib/graphs/__init__.py,sha256=wlYIiXTuZRE6Kx3K0RpPUoZikzoegBuN2hcdqMODtGk,2433
44
45
  job_shop_lib/graphs/_build_disjunctive_graph.py,sha256=UbUYdeQaaeEqLchcKJGHEFGl4wElfGLb1o_R-u8wqnA,5120
45
46
  job_shop_lib/graphs/_build_resource_task_graphs.py,sha256=vIy_EkQjgQAd5YyJxKAuGf7CLTjgCfhz-fYrObF4DTU,6962
@@ -51,6 +52,11 @@ job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py,sha256=-t0T8W-J
51
52
  job_shop_lib/graphs/graph_updaters/_graph_updater.py,sha256=j1f7iWsa62GVszK2BPaMxnKBCEGWa9owm8g4VWUje8w,1967
52
53
  job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py,sha256=9NG3pu7Z5h-ZTfX8rRiZbI_NfNQi80h-XUHainshjZY,6064
53
54
  job_shop_lib/graphs/graph_updaters/_utils.py,sha256=sdw2Vo75P9c6Fy-YBlfgpXb9gPwHUluTB1E-9WINm_g,730
55
+ job_shop_lib/metaheuristics/__init__.py,sha256=z7bHN5vP-ctOSL0eUYK-aRyhRkU2lrDyA4kBs15EME0,1884
56
+ job_shop_lib/metaheuristics/_job_shop_annealer.py,sha256=Ty4SLPZh1NrL-XRqU76EeN8fwUdKfqbphqfYEDje1lQ,9195
57
+ job_shop_lib/metaheuristics/_neighbor_generators.py,sha256=3RePlnYvJdpdhObmf0m_3NhyUM7avfNr4vOZT0PWTRQ,6563
58
+ job_shop_lib/metaheuristics/_objective_functions.py,sha256=GG5M3LoLnNzo1zxzfpNMvo4bdYlqWuhVA8mIkXFsxxM,2607
59
+ job_shop_lib/metaheuristics/_simulated_annealing_solver.py,sha256=EMCrFl2zzJubrvCMi5upm8lnUgtBizhZbi4EvbnIsM4,6200
54
60
  job_shop_lib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
61
  job_shop_lib/reinforcement_learning/__init__.py,sha256=sAVgxylKfBnn2rrz0BFcab1kjvQQ1h-hgldfbkPF--E,1537
56
62
  job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py,sha256=6nXw67Tfmim3LqlSuQ9Cfg3mMY-VmbMHuXfyOL90jng,15740
@@ -67,7 +73,7 @@ job_shop_lib/visualization/gantt/_plot_gantt_chart.py,sha256=_4UGUTRuIw0tLzsJD9G
67
73
  job_shop_lib/visualization/graphs/__init__.py,sha256=HUWzfgQLeklNROtjnxeJX_FIySo_baTXO6klx0zUVpQ,630
68
74
  job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py,sha256=L9_ZGgvCFpGc2rTOdZESdtydFQqShjqedimIOhqZx6Y,16209
69
75
  job_shop_lib/visualization/graphs/_plot_resource_task_graph.py,sha256=nkkdZ-9_OBevw72Frecwzv1y3WyhGZ9r9lz0y9MXvZ8,13192
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,,
76
+ job_shop_lib-1.6.0.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
77
+ job_shop_lib-1.6.0.dist-info/METADATA,sha256=cDfJquD-2ph8c7Niv0_hZ_OGI_1fOmJLe-nNoXVS96s,19250
78
+ job_shop_lib-1.6.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
79
+ job_shop_lib-1.6.0.dist-info/RECORD,,