### Set up ALNS Project with Examples using Poetry Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/installation.rst Navigates into the ALNS repository, sets up a virtual environment, and installs all dependencies, including the optional 'examples' group and all extras, using Poetry. This command ensures all necessary packages for running the example notebooks are installed. ```shell cd ALNS poetry install --with examples --all-extras ``` -------------------------------- ### Install Poetry Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/installation.rst Installs or upgrades the Poetry dependency management tool. Poetry is required for setting up the virtual environment and managing dependencies for running ALNS examples locally. ```shell pip install --upgrade poetry ``` -------------------------------- ### Clone ALNS Repository Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/installation.rst Clones the ALNS GitHub repository to your local machine. This is the first step to running the example notebooks locally. ```shell git clone https://github.com/N-Wouda/ALNS.git ``` -------------------------------- ### Run Jupyter Notebooks with Poetry Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/installation.rst Executes the Jupyter Notebook server using Poetry. This command opens a web interface in your browser, allowing you to access and run the example notebooks located in the 'examples/' folder. ```shell poetry run jupyter notebook ``` -------------------------------- ### ALNS Quickstart Template in Python Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/template.rst This Python code snippet demonstrates a basic template for using the ALNS library. It includes a placeholder `ProblemState` class, functions for `initial_state`, `destroy`, and `repair` operators, and the core ALNS algorithm setup and execution. Users need to implement the TODO sections for their specific problem. ```python from alns import ALNS from alns.accept import HillClimbing from alns.select import RandomSelect from alns.stop import MaxRuntime import numpy.random as rnd class ProblemState: # TODO add attributes that encode a solution to the problem instance def objective(self) -> float: # TODO implement the objective function pass def get_context(self): # TODO implement a method returning a context vector. This is only # needed for some context-aware bandit selectors from MABWiser; # if you do not use those, this default is already sufficient! return None def initial_state() -> ProblemState: # TODO implement a function that returns an initial solution pass def destroy(current: ProblemState, rng: rnd.Generator) -> ProblemState: # TODO implement how to destroy the current state, and return the destroyed # state. Make sure to (deep)copy the current state before modifying! pass def repair(destroyed: ProblemState, rng: rnd.Generator) -> ProblemState: # TODO implement how to repair a destroyed state, and return it pass # Create the initial solution init_sol = initial_state() print(f"Initial solution objective is {init_sol.objective()}.") # Create ALNS and add one or more destroy and repair operators alns = ALNS(rnd.default_rng(seed=42)) alns.add_destroy_operator(destroy) alns.add_repair_operator(repair) # Configure ALNS select = RandomSelect(num_destroy=1, num_repair=1) # see alns.select for others accept = HillClimbing() # see alns.accept for others stop = MaxRuntime(60) # 60 seconds; see alns.stop for others # Run the ALNS algorithm result = alns.iterate(init_sol, select, accept, stop) # Retrieve the final solution best = result.best_state print(f"Best heuristic solution objective is {best.objective()}.") ``` -------------------------------- ### Install ALNS with Poetry Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/contributing.rst Change into the ALNS directory and install project dependencies, including examples and all extras, using Poetry. This command also upgrades Poetry to the latest version. ```shell cd ALNS pip install --upgrade poetry poetry install --with examples --all-extras ``` -------------------------------- ### Skip Example Notebook Execution during Build Source: https://github.com/n-wouda/alns/blob/master/docs/README.md Builds the documentation while skipping the execution of example notebooks. This is achieved by setting the `SKIP_NOTEBOOKS` environmental variable before running the make command, which can significantly speed up local builds. ```shell SKIP_NOTEBOOKS=1 poetry run make html --directory=docs ``` -------------------------------- ### Install ALNS from Source using pip Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/installation.rst Installs the latest version of ALNS directly from its GitHub repository using pip. This method is useful for accessing updates not yet published to the Python Package Index. ```shell pip install 'alns @ git+https://github.com/N-Wouda/alns' ``` -------------------------------- ### Show ALNS Installation Versions (Python) Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/getting_help.rst This Python code snippet imports the 'alns' library and calls the `show_versions()` function to display detailed information about the ALNS installation and its dependencies. This is useful for debugging and reporting issues. ```python import alns alns.show_versions() ``` -------------------------------- ### Install Documentation Dependencies with Poetry Source: https://github.com/n-wouda/alns/blob/master/docs/README.md Installs the optional 'docs' dependency group for building documentation. This ensures all necessary packages for Sphinx and related tools are available. ```shell poetry install --with docs ``` ```shell poetry install --only docs ``` -------------------------------- ### Install ALNS using pip Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/installation.rst Installs the ALNS package using pip, the standard Python package installer. This is the most straightforward method for incorporating the library into your project. ```shell pip install alns ``` -------------------------------- ### Show ALNS Installation Versions (Shell) Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/getting_help.rst This shell command executes a Python script inline to display version information for the ALNS installation and its dependencies. It's a convenient way to quickly gather version details from the command line. ```shell python -c 'import alns; alns.show_versions()' ``` -------------------------------- ### Install Pre-commit Hooks Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/contributing.rst Install the pre-commit configuration to automatically check for style and typing issues before each commit. This is an optional step for contributors who use pre-commit. ```shell pre-commit install ``` -------------------------------- ### Run ALNS Test Suite with Poetry Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/contributing.rst Execute the project's test suite using Poetry to ensure the local installation is running smoothly. This command is used after installing dependencies. ```shell poetry run pytest ``` -------------------------------- ### Install ALNS Package Source: https://github.com/n-wouda/alns/blob/master/README.md Installs the ALNS package using pip. This command installs the core ALNS library, which has dependencies on numpy and matplotlib. For advanced features like multi-armed bandit algorithms for operator selection, an optional dependency can be included. ```bash pip install alns ``` ```bash pip install alns[mabwiser] ``` -------------------------------- ### Using LinGreedy Contextual Bandit with ALNS in Python Source: https://github.com/n-wouda/alns/blob/master/examples/alns_features.ipynb Example of initializing and using the LinGreedy contextual bandit policy with MABSelector in ALNS. It shows the setup for an ALNS iteration and printing the best objective found. ```python select = MABSelector( scores=[5, 2, 1, 0.5], num_destroy=2, num_repair=1, learning_policy=LearningPolicy.LinGreedy(epsilon=0.15), ) alns = make_alns() res = alns.iterate(init_sol, select, accept, MaxIterations(10_000)) print(f"Found solution with objective {-res.best_state.objective()}.") ``` -------------------------------- ### Printing Solution Comparison to Optimal Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Prints a detailed comparison of the heuristic solution's objective value against the known optimal value, highlighting the difference in beams used. Dependencies include the OPTIMAL_BEAMS constant. ```python obj = solution.objective() print( "Number of beams used is {0}, which is {1} more than the optimal value {2}.".format( obj, obj - OPTIMAL_BEAMS, OPTIMAL_BEAMS ) ) ``` -------------------------------- ### Import ALNS and Dependencies (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Imports necessary libraries for the ALNS meta-heuristic, including copy, matplotlib, numpy, and specific ALNS modules for acceptance criteria, selection, and stopping conditions. ```python import copy from functools import partial import matplotlib.pyplot as plt import numpy as np import numpy.random as rnd from alns import ALNS from alns.accept import HillClimbing from alns.select import RouletteWheel from alns.stop import MaxIterations ``` -------------------------------- ### Clone ALNS Repository Source: https://github.com/n-wouda/alns/blob/master/docs/source/setup/contributing.rst Clone your forked ALNS repository to your local environment. This is the first step in setting up a local installation for development. ```shell git clone https://github.com//ALNS.git ``` -------------------------------- ### Run ALNS with Knapsack Problem Example (Python) Source: https://context7.com/n-wouda/alns/llms.txt Demonstrates how to use the ALNS class to solve a knapsack problem. It defines a custom state, destroy, and repair operators, then configures and runs the ALNS algorithm with specified selection, acceptance, and stopping criteria. ```python import copy import numpy.random as rnd from alns import ALNS from alns.accept import SimulatedAnnealing from alns.select import RouletteWheel from alns.stop import MaxIterations # Define a solution state with objective function (required interface) class KnapsackState: def __init__(self, items, selected, capacity, weights, values): self.items = items self.selected = selected self.capacity = capacity self.weights = weights self.values = values def objective(self) -> float: """Return negative value for maximization (ALNS minimizes).""" return -sum(self.values[i] for i in self.selected) def total_weight(self): return sum(self.weights[i] for i in self.selected) # Define destroy operator - removes items from solution def random_destroy(state: KnapsackState, rng: rnd.Generator) -> KnapsackState: destroyed = copy.deepcopy(state) if destroyed.selected: num_remove = max(1, len(destroyed.selected) // 3) to_remove = rng.choice(list(destroyed.selected), size=num_remove, replace=False) destroyed.selected = destroyed.selected - set(to_remove) return destroyed # Define repair operator - adds items back greedily def greedy_repair(state: KnapsackState, rng: rnd.Generator) -> KnapsackState: candidates = [i for i in state.items if i not in state.selected] # Sort by value/weight ratio (best first) candidates.sort(key=lambda i: state.values[i] / state.weights[i], reverse=True) for item in candidates: if state.total_weight() + state.weights[item] <= state.capacity: state.selected.add(item) return state # Problem data weights = [10, 20, 30, 40, 50] values = [60, 100, 120, 140, 160] capacity = 80 items = list(range(len(weights))) # Create initial solution init_state = KnapsackState(items, set(), capacity, weights, values) init_state = greedy_repair(copy.deepcopy(init_state), rnd.default_rng(42)) print(f"Initial objective: {init_state.objective()}") # Output: Initial objective: -260 # Configure and run ALNS alns = ALNS(rnd.default_rng(seed=42)) alns.add_destroy_operator(random_destroy) alns.add_repair_operator(greedy_repair) select = RouletteWheel([3, 2, 1, 0.5], decay=0.8, num_destroy=1, num_repair=1) accept = SimulatedAnnealing(start_temperature=100, end_temperature=1, step=0.99) stop = MaxIterations(1000) result = alns.iterate(init_state, select, accept, stop) print(f"Best objective: {result.best_state.objective()}") # Output: Best objective: -260 print(f"Selected items: {result.best_state.selected}") # Output: Selected items: {0, 1, 2} ``` -------------------------------- ### Enable Matplotlib Inline Plotting (Jupyter) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Configures matplotlib to display plots directly within Jupyter notebooks or similar environments. ```python %matplotlib inline ``` -------------------------------- ### Calculate Number of Beams to Remove (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Calculates the number of beam assignments to remove based on the total number of beams in the current solution and the degree of destruction. ```python def beams_to_remove(num_beams): return int(num_beams * degree_of_destruction) ``` -------------------------------- ### Calculate Wastage for a Beam Assignment (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Calculates the amount of wasted material on a single beam given its assignment of ordered beams. ```python def wastage(assignment): """ Helper method that computes the wastage on a given beam assignment. """ return BEAM_LENGTH - sum(assignment) ``` -------------------------------- ### Define Degree of Destruction (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Sets a parameter that controls the proportion of the current solution to be destroyed by the destroy operators. ```python degree_of_destruction = 0.25 ``` -------------------------------- ### Define Random Seed (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Sets a constant for the random seed to ensure reproducibility of random number generation. ```python SEED = 5432 ``` -------------------------------- ### ALNS Iteration and Solution Analysis Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Runs the ALNS heuristic using specified selection, acceptance, and stopping criteria. It then extracts the best solution found and prints its objective value. Dependencies include ALNS, HillClimbing, RouletteWheel, MaxIterations, and plotting functions. ```python accept = HillClimbing() select = RouletteWheel([3, 2, 1, 0.5], 0.8, 2, 2) stop = MaxIterations(5_000) result = alns.iterate(init_sol, select, accept, stop) solution = result.best_state objective = solution.objective() ``` -------------------------------- ### Autofit Simulated Annealing Parameters in Python Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Automatically determines the start temperature, final temperature, and updating step for the SimulatedAnnealing acceptance criterion. This method requires the initial objective value, a probability for accepting a worse solution, a probability for accepting a better solution, and the total number of iterations. ```python accept = SimulatedAnnealing.autofit(init.objective(), 0.05, 0.50, ITERS) ``` -------------------------------- ### Initialize ALNS and Add Operators (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/travelling_salesman_problem.ipynb Initializes the ALNS meta-heuristic framework and adds various destroy and repair operators. This setup defines the neighborhood exploration strategy for the search algorithm. Dependencies include the ALNS library and pre-defined operator functions. ```python alns = ALNS(rnd.default_rng(SEED)) alns.add_destroy_operator(random_removal) alns.add_destroy_operator(path_removal) alns.add_destroy_operator(worst_removal) alns.add_repair_operator(greedy_repair) ``` -------------------------------- ### Plotting Final Solution Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Generates a plot representing the final solution obtained by the ALNS heuristic, likely visualizing the assignments of beams. ```python solution.plot() ``` -------------------------------- ### Practical ALNS Callback: Local Search on Best Solutions Source: https://context7.com/n-wouda/alns/llms.txt Provides a practical example of an ALNS callback function, `local_search_on_best`, which applies a 2-opt local search whenever a new best solution is found. This demonstrates how callbacks can be used to enhance the search process by performing additional optimization steps. ```python import copy import numpy.random as rnd from alns import ALNS alns = ALNS(rnd.default_rng(42)) # Practical example: local search on best solutions def local_search_on_best(state, rng): """Apply 2-opt local search when new best TSP tour is found.""" improved = True while improved: improved = False for i in range(len(state.nodes) - 1): for j in range(i + 2, len(state.nodes)): # Check if reversing segment [i+1, j] improves tour # (implementation depends on problem structure) pass alns.on_best(local_search_on_best) ``` -------------------------------- ### ALNS Acceptance Criteria Examples in Python Source: https://context7.com/n-wouda/alns/llms.txt Demonstrates the usage of various acceptance criteria from the `alns.accept` module. These criteria define the conditions under which a new candidate solution replaces the current one, ranging from strict improvement (Hill Climbing) to probabilistic and history-based methods. ```python from alns.accept import ( SimulatedAnnealing, HillClimbing, GreatDeluge, RecordToRecordTravel, LateAcceptanceHillClimbing, AlwaysAccept, ) # Hill Climbing - only accepts improving solutions hill_climbing = HillClimbing() # Accepts if: candidate.objective() <= current.objective() # Simulated Annealing - probabilistic acceptance with cooling schedule sa = SimulatedAnnealing( start_temperature=100.0, # Initial temperature end_temperature=1.0, # Final temperature step=0.99, # Decay factor (exponential) method="exponential" # or "linear" ) # Accepts with probability: exp((current_obj - candidate_obj) / temperature) # Auto-fit Simulated Annealing based on problem characteristics sa_auto = SimulatedAnnealing.autofit( init_obj=1000, # Initial solution objective worse=0.05, # Accept 5% worse solutions initially accept_prob=0.5, # With 50% probability num_iters=10000, # Total iterations method="exponential" ) # Great Deluge - threshold-based acceptance great_deluge = GreatDeluge( alpha=1.1, # Initial threshold = alpha * initial_objective beta=0.01 # Threshold update rate ) # Accepts if: candidate.objective() < threshold # Record-to-Record Travel - gap-based acceptance rrt = RecordToRecordTravel( start_threshold=50.0, end_threshold=0.0, step=0.1, method="linear", cmp_best=True # Compare against best (False for threshold accepting) ) # Accepts if: candidate.objective() - baseline.objective() <= threshold # Auto-fit RRT rrt_auto = RecordToRecordTravel.autofit( init_obj=1000, start_gap=0.05, # 5% initial gap end_gap=0.0, # 0% final gap num_iters=10000, method="linear" ) # Late Acceptance Hill Climbing - history-based acceptance lahlc = LateAcceptanceHillClimbing(history_length=100) # Accepts if: candidate.objective() <= objective from 100 iterations ago # Always Accept - useful for testing always = AlwaysAccept() ``` -------------------------------- ### Plotting ALNS Objective Convergence Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Generates a plot showing the convergence of the objective function over the ALNS iterations. This helps visualize the performance of the heuristic. Dependencies include matplotlib and the ALNS result object. ```python _, ax = plt.subplots(figsize=(12, 6)) result.plot_objectives(ax=ax) ``` -------------------------------- ### Printing Final Solution Objective Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Prints the objective value of the final heuristic solution obtained by the ALNS algorithm. This provides a key performance metric. ```python print("Heuristic solution has objective value:", solution.objective()) ``` -------------------------------- ### Import Libraries for ALNS and TSP Source: https://github.com/n-wouda/alns/blob/master/examples/travelling_salesman_problem.ipynb Imports necessary libraries for ALNS, TSP data handling, numerical operations, and plotting. This setup is crucial for all subsequent operations. ```python import copy import matplotlib.pyplot as plt import networkx as nx import numpy as np import numpy.random as rnd import tsplib95 import tsplib95.distances as distances from alns import ALNS from alns.accept import HillClimbing from alns.select import RouletteWheel from alns.stop import MaxRuntime ``` -------------------------------- ### Define Optimal Beams Constant (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Sets a constant representing the known optimal number of beams required for the specific cutting stock problem instance. ```python OPTIMAL_BEAMS = 74 ``` -------------------------------- ### ALNS Initialization and Operator Addition Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Initializes an ALNS object, adds destroy operators (random_removal, worst_removal), and repair operators (greedy_insert, minimal_wastage). This sets up the core components for the ALNS heuristic. Dependencies include the ALNS class and specific operator functions. ```python alns = ALNS(rng) alns.add_destroy_operator(random_removal) alns.add_destroy_operator(worst_removal) alns.add_repair_operator(greedy_insert) alns.add_repair_operator(minimal_wastage) ``` -------------------------------- ### Configure ALNS with Operators in Python Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Initializes an ALNS object and adds destroy and repair operators. This example adds 'random_removal' and 'adjacent_removal' as destroy operators and 'greedy_repair_then_local_search' as the repair operator. ```python alns = ALNS(rnd.default_rng(SEED)) alns.add_destroy_operator(random_removal) alns.add_destroy_operator(adjacent_removal) alns.add_repair_operator(greedy_repair_then_local_search) ``` -------------------------------- ### Generate Initial RCPSP Solution Source: https://github.com/n-wouda/alns/blob/master/examples/resource_constrained_project_scheduling_problem.ipynb Creates an initial solution for the RCPSP by ordering all jobs topologically. This serves as the starting point for the ALNS heuristic. It assumes an `instance` object is available. ```python init_sol = RcpspState(list(range(instance.num_jobs))) print(f"Initial solution has objective {init_sol.objective()}.") ``` -------------------------------- ### Load Cutting Stock Problem Data (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Reads data for the cutting stock problem from a file. It parses the number of lines for beam orders, the length of available beams, and a list of ordered beams with their lengths and amounts. ```python # The first line lists the number of lines for beam orders. # The second line is the length of the available beams. Each # following line is an order of (length, amount) tuples. with open("data/640.csp") as file: data = file.readlines() NUM_LINES = int(data[0]) BEAM_LENGTH = int(data[1]) # Beams to be cut from the available beams BEAMS = [ int(length) for datum in data[-NUM_LINES:] for length, amount in [datum.strip().split()] for _ in range(int(amount)) ] print("Each available beam is of length:", BEAM_LENGTH) print("Number of beams to be cut (orders):", len(BEAMS)) ``` -------------------------------- ### CspState Class for Cutting Stock Problem (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Defines the state representation for the cutting stock problem. It includes assignments of ordered beams to available beams and a list of unassigned beams. Includes methods for copying state, calculating the objective function (number of beams used), and plotting the solution. ```python class CspState: """ Solution state for the CSP problem. It has two data members, assignments and unassigned. Assignments is a list of lists, one for each beam in use. Each entry is another list, containing the ordered beams cut from this beam. Each such sublist must sum to at most BEAM_LENGTH. Unassigned is a list of ordered beams that are not currently assigned to one of the available beams. """ def __init__(self, assignments, unassigned=None): self.assignments = assignments self.unassigned = [] if unassigned is not None: self.unassigned = unassigned def copy(self): """ Helper method to ensure each solution state is immutable. """ return CspState( copy.deepcopy(self.assignments), self.unassigned.copy() ) def objective(self): """ Computes the total number of beams in use. """ return len(self.assignments) def plot(self): """ Helper method to plot a solution. """ _, ax = plt.subplots(figsize=(12, 6)) ax.barh( np.arange(len(self.assignments)), [sum(assignment) for assignment in self.assignments], height=1, ) ax.set_xlim(right=BEAM_LENGTH) ax.set_yticks(np.arange(len(self.assignments), step=10)) ax.margins(x=0, y=0) ax.set_xlabel("Usage") ax.set_ylabel("Beam (#)") plt.draw_if_interactive() ``` -------------------------------- ### Random Removal Destroy Operator (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Implements a destroy operator that randomly selects and removes a specified number of beam assignments from the current solution state. The removed beams are added to the unassigned list. ```python def random_removal(state, rng): """ Iteratively removes randomly chosen beam assignments. """ state = state.copy() for _ in range(beams_to_remove(state.objective())): idx = rng.integers(state.objective()) state.unassigned.extend(state.assignments.pop(idx)) return state ``` -------------------------------- ### Plot Gantt Chart for Flow Shop Schedule in Python Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Generates and displays a Gantt chart for a given schedule in the permutation flow shop problem. It uses matplotlib to visualize job start and completion times on different machines. ```python import matplotlib.pyplot as plt import numpy as np import random as rnd from typing import List, Tuple, Optional # Assuming DATA, compute_completion_times, and compute_makespan are defined def plot(schedule, name): """ Plots a Gantt chart of the schedule for the permutation flow shop problem. """ n_machines, n_jobs = DATA.processing_times.shape completion = compute_completion_times(schedule) start = completion - DATA.processing_times # Plot each job using its start and completion time cmap = plt.colormaps["rainbow"].resampled(n_jobs) machines, length, start_job, job_colors = zip( *[ (i, DATA.processing_times[i, j], start[i, j], cmap(j - 1)) for i in range(n_machines) for j in range(n_jobs) ] ) _, ax = plt.subplots(1, figsize=(12, 6)) ax.barh(machines, length, left=start_job, color=job_colors) ax.set_title(f"{name}\n Makespan: {compute_makespan(schedule)}") ax.set_ylabel(f"Machine") ax.set_xlabel(f"Completion time") ax.set_yticks(range(DATA.n_machines)) ax.set_yticklabels(range(1, DATA.n_machines + 1)) ax.invert_yaxis() plt.show() # Example usage: # plot( # rnd.choice(range(DATA.n_jobs), size=DATA.n_jobs, replace=False), # "A random schedule", # ) ``` -------------------------------- ### Plotting ALNS Operator Counts Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Visualizes the usage counts of different destroy and repair operators during the ALNS run. This aids in diagnosing operator performance. Dependencies include matplotlib and the ALNS result object. ```python figure = plt.figure("operator_counts", figsize=(12, 6)) figure.subplots_adjust(bottom=0.15, hspace=0.5) result.plot_operator_counts(figure, title="Operator diagnostics") ``` -------------------------------- ### Minimal Wastage Repair Operator Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Implements a repair operator that finds the insertion point for each unassigned beam that minimizes the resulting beam wastage. It uses a helper function 'insertion_cost' to calculate the cost for each potential insertion. Dependencies include a 'state' object and a 'wastage' function. ```python def minimal_wastage(state, rng): """ For every unassigned ordered beam, the operator determines which beam would minimise that beam's waste once the ordered beam is inserted. """ def insertion_cost(assignment, beam): # helper method for min if beam <= wastage(assignment): return wastage(assignment) - beam return float("inf") while len(state.unassigned) != 0: beam = state.unassigned.pop(0) assignment = min( state.assignments, key=partial(insertion_cost, beam=beam) ) if beam <= wastage(assignment): assignment.append(beam) else: state.assignments.append([beam]) return state ``` -------------------------------- ### Greedy Insert Repair Operator Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Implements a greedy repair operator that inserts unassigned beams into the first available assignment that fits. It shuffles unassigned beams before insertion. Dependencies include a 'state' object with 'unassigned' beams and 'assignments', and a 'wastage' function. ```python def greedy_insert(state, rng): """ Inserts the unassigned beams greedily into the first fitting beam. Shuffles the unassigned ordered beams before inserting. """ rng.shuffle(state.unassigned) while len(state.unassigned) != 0: beam = state.unassigned.pop(0) for assignment in state.assignments: if beam <= wastage(assignment): assignment.append(beam) break else: state.assignments.append([beam]) return state ``` -------------------------------- ### Build Documentation with Makefile Source: https://github.com/n-wouda/alns/blob/master/docs/README.md Builds the project documentation using the Makefile. The `html` target generates the documentation in HTML format, mirroring the ReadTheDocs build. ```shell poetry run make help ``` ```shell poetry run make html ``` ```shell poetry run make html --directory=docs ``` -------------------------------- ### ALNS Heuristic Operator Setup Source: https://github.com/n-wouda/alns/blob/master/examples/capacitated_vehicle_routing_problem.ipynb Configures the Adaptive Large Neighborhood Search (ALNS) heuristic by adding a 'random_removal' destroy operator and a 'greedy_repair' repair operator. This setup is a basic configuration, as ALNS typically supports multiple operators for adaptive selection. ```python alns = ALNS(rnd.default_rng(SEED)) alns.add_destroy_operator(random_removal) alns.add_repair_operator(greedy_repair) ``` -------------------------------- ### Load RCPSP Instance from File Source: https://github.com/n-wouda/alns/blob/master/examples/resource_constrained_project_scheduling_problem.ipynb Demonstrates how to load an RCPSP instance from a specified file path using the `read_instance` class method of the `ProblemData` dataclass. This method parses data in the PSPLib format. ```python instance = ProblemData.read_instance("data/j9041_6.sm") ``` -------------------------------- ### Plotting Initial Solution Schedule Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Generates a plot visualizing the schedule obtained from the NEH initialization. ```python plot(init.schedule, "NEH") ``` -------------------------------- ### Simulated Annealing Acceptance Criterion for ALNS in Python Source: https://github.com/n-wouda/alns/blob/master/examples/alns_features.ipynb Example of using the Simulated Annealing acceptance criterion in ALNS. This method uses a temperature parameter to control the probability of accepting worse solutions, with exponential decay. ```python accept = SimulatedAnnealing( start_temperature=1_000, end_temperature=1, step=1 - 1e-3, method="exponential", ) alns = make_alns() res = alns.iterate(init_sol, select, accept, MaxIterations(10_000)) print(f"Found solution with objective {-res.best_state.objective()}.") ``` -------------------------------- ### Load CVRP Instance and Best Known Solution using vrplib Source: https://github.com/n-wouda/alns/blob/master/examples/capacitated_vehicle_routing_problem.ipynb Reads a CVRP instance file and its corresponding best-known solution file using the vrplib package. The data is loaded into a dictionary-like object, and the best-known solution is parsed into a SimpleNamespace object. Assumes unlimited vehicles are available. ```python import vrplib from types import SimpleNamespace data = vrplib.read_instance("data/ORTEC-n242-k12.vrp") bks = SimpleNamespace(**vrplib.read_solution("data/ORTEC-n242-k12.sol")) ``` -------------------------------- ### Load Taillard Instance Data using Python Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Loads permutation flow shop benchmark instances from a file. It parses job count, machine count, best known value, and processing times using Python's dataclasses and numpy. ```python from dataclasses import dataclass import numpy as np @dataclass class Data: n_jobs: int n_machines: int bkv: int # best known value processing_times: np.ndarray @classmethod def from_file(cls, path): with open(path, "r") as fi: lines = fi.readlines() n_jobs, n_machines, _, bkv, _ = [ int(num) for num in lines[1].split() ] processing_times = np.genfromtxt(lines[3:], dtype=int) return cls(n_jobs, n_machines, bkv, processing_times) DATA = Data.from_file("data/tai50_20_8.txt") ``` -------------------------------- ### Worst Removal Destroy Operator (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/cutting_stock_problem.ipynb Implements a destroy operator that removes beam assignments with the most wastage. It sorts the assignments by wastage in descending order and removes the top ones, adding them to the unassigned list. ```python def worst_removal(state, rng): """ Removes beams in decreasing order of wastage, such that the poorest assignments are removed first. """ state = state.copy() # Sort assignments by wastage, worst first state.assignments.sort(key=wastage, reverse=True) # Removes the worst assignments for _ in range(beams_to_remove(state.objective())): state.unassigned.extend(state.assignments.pop(0)) return state ``` -------------------------------- ### Compute Insertion Costs using Taillard's Acceleration in Python Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Efficiently computes the makespan resulting from inserting a job at all possible positions in a given schedule using Taillard's acceleration algorithm. This reduces the complexity from O(n^2m) to O(nm). ```python import numpy as np from typing import List, Tuple # Assuming DATA is defined def all_insert_cost(schedule: List[int], job: int) -> List[Tuple[int, float]]: """ Computes all partial makespans when inserting a job in the schedule. O(nm) using Taillard's acceleration. Returns a list of tuples of the insertion index and the resulting makespan. [1] Taillard, E. (1990). Some efficient heuristic methods for the flow shop sequencing problem. European Journal of Operational Research, 47(1), 65-74. """ k = len(schedule) + 1 m = DATA.processing_times.shape[0] p = DATA.processing_times # Earliest completion of schedule[j] on machine i before insertion e = np.zeros((m + 1, k)) for j in range(k - 1): # This part of the code is incomplete in the provided snippet. ``` -------------------------------- ### ALNS Configuration with AlphaUCB and MaxIterations Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Configures the ALNS algorithm by setting the maximum number of iterations and initializing the selection strategy using AlphaUCB with predefined scores and parameters. ```python ITERS = 8000 init = NEH(DATA.processing_times) select = AlphaUCB( scores=[5, 2, 1, 0.5], alpha=0.05, num_destroy=len(alns.destroy_operators), num_repair=len(alns.repair_operators), ) stop = MaxIterations(ITERS) ``` -------------------------------- ### Adjacent Removal Destroy Operator Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Implements a destroy operator that removes a specified number of adjacent jobs from the solution's schedule. It randomly selects a starting point for removal and ensures jobs are moved to the unassigned list. ```python def adjacent_removal(state: Solution, rng, n_remove=2) -> Solution: """ Randomly remove a number adjacent jobs from the solution. """ destroyed = deepcopy(state) start = rng.integers(DATA.n_jobs - n_remove) jobs_to_remove = [state.schedule[start + idx] for idx in range(n_remove)] for job in jobs_to_remove: destroyed.unassigned.append(job) destroyed.schedule.remove(job) return destroyed ``` -------------------------------- ### Run ALNS Iteration and Get Best Solution Source: https://github.com/n-wouda/alns/blob/master/examples/resource_constrained_project_scheduling_problem.ipynb Executes the ALNS heuristic for a specified number of iterations using a segmented roulette wheel selection strategy and a hill-climbing acceptance criterion. It then retrieves and prints the objective value of the best solution found. ```python select = SegmentedRouletteWheel(WEIGHTS, THETA, SEG_LENGTH, 3, 1) accept = HillClimbing() stop = MaxIterations(ITERS) res = alns.iterate(init_sol, select, accept, stop) sol = res.best_state print(f"Heuristic solution has objective {sol.objective()}.") ``` -------------------------------- ### Visualize Tuning Results (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Generates a plot to visualize the final objective value against different 'n_destroy' values. This helps in identifying the optimal 'n_destroy' parameter based on the experimental results. ```python fig, ax = plt.subplots(figsize=[8, 6]) ax.plot(*zip(*sorted(objectives.items()))) ax.set_title("Final objective value for various n_destroy values") ax.set_ylabel("Objective value") ax.set_xlabel("n_destroy"); ``` -------------------------------- ### ALNS Initialization and Objective Calculation Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Initializes a solution using the NEH heuristic and calculates its objective value. It then computes and prints the percentage difference from the best known value. ```python init = NEH(DATA.processing_times) objective = init.objective() pct_diff = 100 * (objective - DATA.bkv) / DATA.bkv print(f"Initial solution objective is {objective}.") print(f"This is {pct_diff:.1f}% worse than the best known value {DATA.bkv}.") ``` -------------------------------- ### Set Up ALNS Iteration Parameters in Python Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Defines the parameters for an ALNS iteration, including the number of iterations, the initial solution generator (NEH), the selection strategy (AlphaUCB), the acceptance criterion (autofitted SimulatedAnnealing), and the stopping condition (MaxIterations). ```python ITERS = 600 # fewer iterations because local search is expensive init = NEH(DATA.processing_times) select = AlphaUCB( scores=[5, 2, 1, 0.5], alpha=0.05, num_destroy=len(alns.destroy_operators), num_repair=len(alns.repair_operators), ) accept = SimulatedAnnealing.autofit(init.objective(), 0.05, 0.50, ITERS) stop = MaxIterations(ITERS) ``` -------------------------------- ### Path Removal Operator for ALNS Source: https://github.com/n-wouda/alns/blob/master/examples/travelling_salesman_problem.ipynb Implements the path removal operator, which removes an entire consecutive sub-path of edges from the current solution. It randomly selects a starting node and iteratively removes edges along the path until the specified number of edges to remove is reached. ```python def path_removal(current, rng): """ Removes an entire consecutive sub-path, that is, a series of contiguous edges. """ destroyed = copy.deepcopy(current) node_idx = rng.choice(len(destroyed.nodes)) node = destroyed.nodes[node_idx] for _ in range(edges_to_remove(current)): node = destroyed.edges.pop(node) return destroyed ``` -------------------------------- ### Core Calculation Logic (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/permutation_flow_shop_problem.ipynb Contains core logic for calculating makespan and related metrics using nested loops and numpy arrays. It computes duration, earliest completion times, and partial makespans. ```python for i in range(m): e[i, j] = max(e[i, j - 1], e[i - 1, j]) + p[i, schedule[j]] # Duration between starting time and final makespan q = np.zeros((m + 1, k)) for j in range(k - 2, -1, -1): for i in range(m - 1, -1, -1): q[i, j] = max(q[i + 1, j], q[i, j + 1]) + p[i, schedule[j]] # Earliest relative completion time f = np.zeros((m + 1, k)) for l in range(k): for i in range(m): f[i, l] = max(f[i - 1, l], e[i, l - 1]) + p[i, job] # Partial makespan; drop the last (dummy) row of q M = np.max(f + q, axis=0) return [(idx, M[idx]) for idx in np.argsort(M)] ``` -------------------------------- ### Most Mobile Removal Operator (Python) Source: https://github.com/n-wouda/alns/blob/master/examples/resource_constrained_project_scheduling_problem.ipynb Implements the 'most mobile removal' operator for ALNS. This operator unschedules jobs that have the most flexibility within the current schedule, defined by their earliest possible start and latest possible finish times based on predecessors and successors. Jobs that are first or last in the overall project are excluded from being considered for removal. ```python import copy import numpy as np # Assuming 'instance', 'Q', 'schedule', 'LB', 'UB' are defined elsewhere # For example: # class MockInstance: # num_jobs = 10 # num_resources = 2 # duration = np.random.rand(10) # resources = np.random.rand(2) # last_job = 9 # first_job = 0 # predecessors = [[] for _ in range(10)] # successors = [[] for _ in range(10)] # # instance = MockInstance() # # def schedule(jobs): # # Mock schedule function # n = len(jobs) # s = np.arange(n) * 2 # u = np.random.rand(n, instance.num_resources) # return s, u # # Q = 3 def most_mobile_removal(state, rng): """ This operator unschedules those jobs that are most mobile, that is, those that can be 'moved' most within the schedule, as determined by their scheduled predecessors and successors. Based on Muller (2009). Muller, LF. 2009. An Adaptive Large Neighborhood Search Algorithm for the Resource-constrained Project Scheduling Problem. In _MIC 2009: The VIII Metaheuristics International Conference_. """ state = copy.copy(state) indices = state.indices # Left and right limits. These are the indices of the job's last # predecessor and first successor in the schedule. That indicates # the extent of the job's movement. ll = np.array( [ np.max(indices[instance.predecessors[job]], initial=0) for job in range(instance.num_jobs) ] ) rl = np.array( [ np.min( indices[instance.successors[job]], initial=instance.num_jobs ) for job in range(instance.num_jobs) ] ) mobility = np.maximum(rl - ll, 0) mobility[[instance.first_job, instance.last_job]] = 0 p = mobility / mobility.sum() for job in rng.choice(instance.num_jobs, Q, replace=False, p=p): state.jobs.remove(job) return state ```