job_shop_lib.metaheuristics¶
Metaheuristic algorithms for solving job shop scheduling problems.
This module provides implementations of various metaheuristic optimization algorithms designed to solve the job shop scheduling problem.
Metaheuristics are particularly well-suited for JSSP due to their ability to:
Handle large solution spaces efficiently
Escape local optima through stochastic mechanisms
Balance exploration and exploitation of the search space
Provide good quality solutions within reasonable computational time
Currently implemented algorithms:
Simulated annealing: A probabilistic technique that accepts worse solutions with decreasing probability to escape local optima
The module aims to contain implementations of other metaheuristic algorithms such as genetic algorithms, particle swarm optimization, tabu search, etc. Feel free to open an issue if you want to contribute!
Helper class for the |
|
Wraps the |
|
|
|
Generates a neighbor schedule by swapping two adjacent operations. |
|
Generates a neighbor by targeting swaps on the critical path. |
|
Generates a neighbor schedule by swapping two random operations. |
|
|
alias of |
Builds an objective function that returns the makespan plus penalties. |
|
Compute the total penalty for deadline violations in a schedule. |
|
Compute the total penalty for due date violations in a schedule. |
- class JobShopAnnealer(instance, initial_state, *, seed=None, neighbor_generator=<function swap_in_critical_path>, objective_function=None)[source]¶
Bases:
AnnealerHelper class for the
SimulatedAnnealingSolver.It uses simanneal as the backend.
In the context of the job shop scheduling problem, simulated annealing is particularly useful for improving previous solutions.
The neighbor move is pluggable via a
neighbor_generatorfunction. By default it usesswap_in_critical_path(), but any function that takes a schedule and a random generator and returns a new schedule can be provided to tailor the exploration of the search space.The process involves iteratively exploring the solution space:
A random move is made to alter the current state. This is done by swapping two operations in the sequence of a machine.
The "energy" of the new state is evaluated using an objective function. With the default objective function, the energy is calculated as the makespan of the schedule plus penalties for any constraint violations (such as deadlines and due dates). See
get_makespan_with_penalties_objective()for details. You can create custom objective functions by implementing theObjectiveFunctioninterface, which takes a schedule and returns a float representing the energy of that schedule.The new state is accepted if it has lower energy (a better solution). If it has higher energy, it might still be accepted with a certain probability, which depends on the current "temperature". The temperature decreases over time, reducing the chance of accepting worse solutions as the algorithm progresses. This helps to avoid getting stuck in local optima.
This is repeated until the solution converges or a maximum number of steps is reached.
Tuning the annealer is crucial for performance. The base
simanneal.Annealerclass provides parameters that can be adjusted:Tmax: Maximum (starting) temperature (default: 25000.0).Tmin: Minimum (ending) temperature (default: 2.5).steps: Number of iterations (default: 50000).updates: Number of progress updates (default: 100).
A good starting point is to set
Tmaxto a value that accepts about 98% of moves andTminto a value where the solution no longer improves. The number ofstepsshould be large enough to explore the search space thoroughly.These parameters can be set on the annealer instance. For example:
annealer.Tmax = 12000.0Alternatively, this class provides an
automethod to find reasonable parameters based on a desired runtime:auto_schedule = annealer.auto(minutes=1)annealer.set_schedule(auto_schedule)- instance¶
The job shop instance to solve.
- random_generator¶
Random generator for reproducibility.
- neighbor_generator¶
Function used to generate neighbors from the current schedule. Defaults to
swap_in_critical_path().
- objective_function¶
Function that computes the energy of the schedule. If
None, it defaults toget_makespan_with_penalties_objective(). This function receives a schedule and returns the energy that will be minimized by the annealer.
- Parameters:
instance (JobShopInstance) -- The job shop instance to solve. It retrieves the jobs and machines from the instance and uses them to create the schedule.
initial_state (Schedule) -- Initial state of the schedule as a list of lists, where each sublist represents the operations of a job.
seed (int | None) -- Random seed for reproducibility. If
None, random behavior will be non-deterministic.neighbor_generator (Callable[[Schedule, Random], Schedule]) -- Function that receives the current schedule and a random generator and returns a new schedule to explore. Defaults to
swap_in_critical_path(). Use this to plug in custom neighborhoods (e.g., adjacent swaps).objective_function (Callable[[Schedule], float] | None) -- Function that computes the energy of the schedule. If
None, it defaults toget_makespan_with_penalties_objective(). This callable receives aScheduleand returns a float that will be minimized by the annealer.
- copy_strategy = 'method'¶
- move()[source]¶
Generates a neighbor state using the configured neighbor generator.
Delegates to
self.neighbor_generatorwith the current schedule and the internal random generator, enabling pluggable neighborhoods.- Return type:
None
- class SimulatedAnnealingSolver(*, initial_temperature=25000, ending_temperature=2.5, steps=50000, updates=100, objective_function=None, seed=None, neighbor_generator=<function swap_in_critical_path>)[source]¶
Bases:
BaseSolverWraps the
JobShopAnnealerto follow the :class`~job_shop_lib.BaseSolver` interface.See also
See the documentation of the
JobShopAnnealerclass for more details on the annealing process.- initial_temperature¶
Initial temperature for the annealing process. It controls the probability of accepting worse solutions. That sets the metropolis criterion. Corresponds to the tmax parameter in the annealer.
- ending_temperature¶
Ending temperature for the annealing process. It controls when to stop accepting worse solutions. Corresponds to the tmin parameter in the annealer.
- steps¶
Number of steps to perform in the annealing process. This is the number of iterations the algorithm will run.
- updates¶
The number of progress updates to print during the annealing process. Set to 0 to disable updates.
- seed¶
Random seed for reproducibility. If
None, random behavior will be non-deterministic.
- Parameters:
initial_temperature (float) -- Initial temperature for the annealing process. It controls the probability of accepting worse solutions. That sets the metropolis criterion. Corresponds to the tmax parameter in the annealer.
ending_temperature (float) -- Ending temperature for the annealing process. It controls when to stop accepting worse solutions. Corresponds to the tmin parameter in the annealer.
steps (int) -- Number of steps to perform in the annealing process. This is the number of iterations the algorithm will run.
updates (int) -- The number of progress updates to print during the annealing process. Set to 0 to disable updates.
seed (int | None) -- Random seed for reproducibility. If
None, random behavior will be non-deterministic.objective_function (Callable[[Schedule], float] | None)
- setup_annealer(instance, initial_state=None)[source]¶
Initializes the annealer with the given instance and initial state.
- Parameters:
instance (JobShopInstance) -- The job shop instance to solve.
initial_state (Schedule | None) -- Initial state of the schedule as a list of lists, where each sublist represents the operations of a job.
- Return type:
None
- solve(instance, initial_state=None)[source]¶
Solves the given Job Shop Scheduling problem using simulated annealing.
- Parameters:
instance (JobShopInstance) -- The job shop problem instance to solve.
initial_state (Schedule | None) -- Initial job sequences for each machine. A job sequence is a list of job ids. Each list of job ids represents the order of operations on the machine. The machine that the list corresponds to is determined by the index of the list. If
None, the solver will generate an initial state using theDispatchingRuleSolver.
- Returns:
The best schedule found.
- Return type:
- swap_adjacent_operations(schedule, random_generator=None)[source]¶
Generates a neighbor schedule by swapping two adjacent operations.
Selects a machine at random with at least two operations and swaps a pair of adjacent operations in its sequence. Internally tries several times to produce a valid neighbor; if none is found, the original schedule is returned.
- swap_in_critical_path(schedule, random_generator=None)[source]¶
Generates a neighbor by targeting swaps on the critical path.
This operator focuses on pairs of consecutive scheduled operations along the current critical path that share the same machine. Swapping such operations directly perturbs the longest-duration chain of precedence and resource constraints that determines the makespan.
Why target the critical path:
The makespan is the length of the critical path; operations not on it typically have slack, so reordering them often does not improve the objective. By contrast, modifying machine order on the critical path can shorten the longest path or unlock constraints that reduce blocking and idle times.
Swapping consecutive critical operations on the same machine always results in a feasible schedule.
Behavior:
Identifies all consecutive pairs on the critical path that run on the same machine and swaps one of them at random.
If no such pairs exist, it falls back to a standard adjacent swap.
- Parameters:
schedule (Schedule) -- Current schedule to perturb.
random_generator (Random | None) -- Source of randomness. If
None, a new generator is created.
- Returns:
A valid neighbor schedule that prioritizes swaps on the critical path, or a neighbor produced by an adjacent swap fallback when none applies.
- Return type:
- swap_random_operations(schedule, random_generator=None)[source]¶
Generates a neighbor schedule by swapping two random operations.
Selects a machine at random with at least two operations and swaps two randomly chosen positions in its sequence. Internally tries several times to produce a valid neighbor; if none is found, the original schedule is returned.
- get_makespan_with_penalties_objective(deadline_penalty_factor=1000000, due_date_penalty_factor=100)[source]¶
Builds an objective function that returns the makespan plus penalties.
This factory returns a callable that evaluates a Schedule as the sum of its makespan and penalties for violating operation-level deadlines and due dates.
Penalties are applied per scheduled operation that finishes after its corresponding attribute value:
Deadline violation: adds
deadline_penalty_factoronce per violating operation (hard constraint surrogate).Due date violation: adds
due_date_penalty_factoronce per violating operation (soft constraint surrogate).
- Parameters:
deadline_penalty_factor (float) -- Cost added for each operation that finishes after its deadline. Defaults to 1_000_000.
due_date_penalty_factor (float) -- Cost added for each operation that finishes after its due date. Defaults to 100.
- Returns:
A function
f(schedule) -> floatthat computesschedule.makespan() + penalty.- Return type:
Callable[[Schedule], float]
Notes
Deadlines and due dates are taken from each operation. If an operation does not define the attribute (
None), no penalty is applied for that attribute.If the instance has neither deadlines nor due dates, the objective is simply the makespan.
- compute_penalty_for_deadlines(schedule, penalty_per_violation)[source]¶
Compute the total penalty for deadline violations in a schedule.
- Parameters:
schedule (Schedule) -- The schedule to evaluate.
penalty_per_violation (float) -- The penalty to apply for each operation that finishes after its deadline.
- Returns:
The total penalty for deadline violations.
- Return type:
float
- compute_penalty_for_due_dates(schedule, penalty_per_violation)[source]¶
Compute the total penalty for due date violations in a schedule.
- Parameters:
schedule (Schedule) -- The schedule to evaluate.
penalty_per_violation (float) -- The penalty to apply for each operation that finishes after its due date.
- Returns:
The total penalty for due date violations.
- Return type:
float