### Run ALNS Example Notebooks with Poetry Source: https://alns.readthedocs.io/en/latest/_sources/setup/installation.rst Starts a Jupyter Notebook server using Poetry to run the example notebooks located in the 'examples/' folder. This command should be run after the development environment has been set up. It requires Poetry and Jupyter Notebook to be installed. ```shell poetry run jupyter notebook ``` -------------------------------- ### Clone ALNS Repository Source: https://alns.readthedocs.io/en/latest/_sources/setup/installation.rst Clones the ALNS GitHub repository to your local machine. This is the first step to running the example notebooks locally. It requires Git to be installed. ```shell git clone https://github.com/N-Wouda/ALNS.git ``` -------------------------------- ### Set up ALNS Development Environment with Poetry Source: https://alns.readthedocs.io/en/latest/_sources/setup/installation.rst Navigates into the ALNS repository, installs all dependencies including optional example dependencies, and sets up a virtual environment using Poetry. This command needs to be run once after cloning the repository. It requires Poetry to be installed. ```shell cd ALNS poetry install --with examples --all-extras ``` -------------------------------- ### Install Poetry Source: https://alns.readthedocs.io/en/latest/_sources/setup/installation.rst Installs or upgrades the Poetry dependency management tool. Poetry is required for setting up the development environment and running examples. It requires pip to be installed. ```shell pip install --upgrade poetry ``` -------------------------------- ### ALNS Quickstart Template in Python Source: https://alns.readthedocs.io/en/latest/_sources/setup/template.rst This Python code snippet demonstrates a basic ALNS setup. It includes a placeholder ProblemState class, initial_state, destroy, and repair functions. It then shows how to instantiate ALNS, add operators, configure selection, acceptance, and stopping criteria, and run the algorithm. ```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 Dependencies with Poetry Source: https://alns.readthedocs.io/en/latest/_sources/setup/contributing.rst Set up the virtual environment and install project dependencies, including optional extras and example components, using Poetry. This command should be run after changing into the ALNS directory. ```shell pip install --upgrade poetry poetry install --with examples --all-extras ``` -------------------------------- ### Show ALNS Installation Versions (Python) Source: https://alns.readthedocs.io/en/latest/_sources/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 ALNS from Source using Pip Source: https://alns.readthedocs.io/en/latest/_sources/setup/installation.rst Installs the ALNS package directly from its GitHub repository using pip. This method is useful for accessing the latest unreleased updates. It requires pip and Git to be installed. ```shell pip install 'alns @ git+https://github.com/N-Wouda/alns' ``` -------------------------------- ### Show ALNS Installation Versions (Shell) Source: https://alns.readthedocs.io/en/latest/_sources/setup/getting_help.rst This shell command executes a Python one-liner to display the ALNS installation and dependency versions. It's a convenient way to quickly gather version information from the command line for bug reporting. ```shell python -c 'import alns; alns.show_versions()' ``` -------------------------------- ### Install pre-commit Hooks Source: https://alns.readthedocs.io/en/latest/_sources/setup/contributing.rst Set up pre-commit hooks for automated style and typing checks. This command should be run after installing the project dependencies to ensure code quality before committing. ```shell pre-commit install ``` -------------------------------- ### Run Test Suite with Poetry Source: https://alns.readthedocs.io/en/latest/_sources/setup/contributing.rst Execute the project's test suite using Poetry to ensure the installation is successful and all components are functioning correctly. This is a crucial step after setting up the local environment. ```shell poetry run pytest ``` -------------------------------- ### Simulated Annealing Acceptance Criterion Setup and Iteration (Python) Source: https://alns.readthedocs.io/en/latest/examples/alns_features Sets up and uses the SimulatedAnnealing acceptance criterion with a maximum number of iterations. It initializes the ALNS, runs the optimization, and prints the best objective found. Dependencies include the ALNS package and its associated functions. ```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()}.") ``` -------------------------------- ### Autofit RecordToRecordTravel Acceptance Criterion Source: https://alns.readthedocs.io/en/latest/api/accept Creates a RecordToRecordTravel object with automatically determined parameters for acceptance thresholds and iterations. It calculates start and end thresholds based on the initial objective and specified gaps, then determines the step size for a given number of iterations and update method. ```python RecordToRecordTravel._autofit(_init_obj : float_, _start_gap : float_, _end_gap : float_, _num_iters : int_, _method : str = 'linear'_) ``` -------------------------------- ### Clone ALNS Repository Source: https://alns.readthedocs.io/en/latest/_sources/setup/contributing.rst Clone your forked ALNS repository to your local machine. This is the first step in setting up a local installation for development. ```shell git clone https://github.com//ALNS.git ``` -------------------------------- ### Implement Epsilon-Greedy Operator Selection with MABWiser in Python Source: https://alns.readthedocs.io/en/latest/examples/alns_features This example demonstrates using an epsilon-greedy policy from MABWiser for operator selection in ALNS. It selects a random operator pair with probability epsilon or the best-performing pair otherwise. Installation of MABWiser is required (`pip install alns[mabwiser]`). ```python select = MABSelector( scores=[5, 2, 1, 0.5], num_destroy=2, num_repair=1, learning_policy=LearningPolicy.EpsilonGreedy(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()}.") ``` -------------------------------- ### Load CVRP Instance and Best Known Solution Source: https://alns.readthedocs.io/en/latest/_sources/examples/capacitated_vehicle_routing_problem.ipynb Loads a CVRP instance and its corresponding best known solution (BKS) using the `vrplib` library. The `ORTEC-n242-k12` instance is used as an example. ```python data = vrplib.read_instance("data/ORTEC-n242-k12.vrp") bks = SimpleNamespace(**vrplib.read_solution("data/ORTEC-n242-k12.sol")) ``` -------------------------------- ### ALNS Iteration Setup with NEH, AlphaUCB, Simulated Annealing, and MaxIterations Source: https://alns.readthedocs.io/en/latest/_sources/examples/permutation_flow_shop_problem.ipynb Sets up and runs an ALNS iteration. It defines the number of iterations, initializes the solution using the NEH heuristic, configures the AlphaUCB selection strategy, sets the acceptance criterion using Simulated Annealing, and defines the stopping condition with 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) result = alns.iterate(init, select, accept, stop) ``` -------------------------------- ### Autofitting Simulated Annealing Acceptance Criteria Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Automatically determines the parameters for the Simulated Annealing acceptance criterion. This method calculates the start temperature, final temperature, and updating step based on the initial solution's objective value, a desired worsening percentage, and the total number of iterations. ```python accept = SimulatedAnnealing.autofit(init.objective(), 0.05, 0.50, ITERS) ``` -------------------------------- ### Random Accept Criterion Implementation Source: https://alns.readthedocs.io/en/latest/api/accept The Random Accept criterion accepts a candidate solution if it improves over the current one, or with a given probability P regardless of the cost. P is updated in each iteration using either a linear or exponential method. The probability P is updated as P←max{Pend, P−γ} for 'linear' or P←max{Pend, γP} for 'exponential', starting from Pstart. ```python class _alns.accept.RandomAccept.RandomAccept(_start_prob : float_, _end_prob : float_, _step : float_, _method : str = 'linear'_): """ The Random Accept criterion accepts a candidate solution if it improves over the current one, or with a given probability P regardless of the cost. P is updated in each iteration as: P←max{Pend, P−γ} when `method = 'linear'`, or P←max{Pend, γP} when `method = 'exponential'`. Initially, P is set to Pstart. Parameters **start_prob** The initial probability Pstart∈[0,1]. **end_prob** The final probability Pend∈[0,1]. **step:** The updating step γ≥0. **method** The updating method, one of {‘linear’, ‘exponential’}. Default ‘linear’. Attributes **end_prob** **method** **start_prob** **step** Methods `__call__`(rng, best, current, candidate) | Call self as a function. ---|--- """ pass ``` -------------------------------- ### Define initial solution for knapsack Source: https://alns.readthedocs.io/en/latest/examples/alns_features Creates a basic initial solution for the knapsack problem where only the first item is selected. This serves as the starting point for the ALNS heuristic. ```python # Terrible - but simple - first solution, where only the first item is # selected. init_sol = KnapsackState(np.zeros(n)) init_sol.x[0] = 1 ``` -------------------------------- ### ALNS Initialization with Destroy and Repair Operators Source: https://alns.readthedocs.io/en/latest/_sources/examples/permutation_flow_shop_problem.ipynb Initializes an ALNS object and adds destroy and repair operators. This example uses '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) ``` -------------------------------- ### Install ALNS Package using pip Source: https://alns.readthedocs.io/en/latest/_sources/index.rst This command installs the ALNS Python package using pip. It requires an active internet connection to download the package from the Python Package Index (PyPI). ```shell pip install alns ``` -------------------------------- ### Compute Serial Schedule and Resource Usage (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/resource_constrained_project_scheduling_problem.ipynb Computes a serial schedule for a given sequence of jobs and the corresponding resource usage. This function, decorated with `lru_cache`, efficiently determines the earliest feasible start time for each job based on precedence constraints and resource availability. It returns the schedule (start times) and the resource usage over time. ```python @lru_cache(32) def schedule(jobs: tuple[int]) -> tuple[np.ndarray, np.ndarray]: """ Computes a serial schedule of the given list of jobs. See Figure 1 in Fleszar and Hindi (2004) for the algorithm. Returns the schedule, and the resources used. Fleszar, K. and K.S. Hindi. 2004. Solving the resource-constrained project scheduling problem by a variable neighbourhood search. _European Journal of Operational Research_. 155 (2): 402 -- 413. """ used = np.zeros((instance.duration.sum(), instance.num_resources)) sched = np.zeros(instance.num_jobs, dtype=int) for job in jobs: pred = instance.predecessors[job] t = max(sched[pred] + instance.duration[pred], default=0) needs = instance.needs[job] duration = instance.duration[job] # This efficiently determines the first feasible insertion point # after t. We compute whether resources are available, and add the # offset s of the first time sufficient are available for the # duration of the job. res_ok = np.all(used[t:] + needs <= instance.resources, axis=1) for s in np.flatnonzero(res_ok): if np.all(res_ok[s : s + duration]): sched[job] = t + s used[t + s : t + s + duration] += needs break return sched, used[: sched[instance.last_job]] ``` -------------------------------- ### MABSelector Operator Selection Source: https://alns.readthedocs.io/en/latest/api/select A selector that utilizes multi-armed bandit algorithms from MABWiser for operator selection. Requires MABWiser to be installed separately. ```APIDOC ## MABSelector ### Description A selector that uses any multi-armed-bandit algorithm from MABWiser. Note that MABWiser is not installed by default and needs to be installed as an extra dependency (`pip install alns[mabwiser]`). ### Method `__init__(self, scores: List[float], num_destroy: int, num_repair: int, learning_policy: LearningPolicyType, neighborhood_policy: Optional[NeighborhoodPolicyType] = None, seed: Optional[int] = None, op_coupling: Optional[ndarray] = None, **kwargs)` ### Parameters #### Path Parameters - **scores** (List[float]) - Required - A list of scores used by the MAB algorithm. - **num_destroy** (int) - Required - Number of destroy operators. - **num_repair** (int) - Required - Number of repair operators. - **learning_policy** (LearningPolicyType) - Required - The learning policy to be used by the MAB algorithm. - **neighborhood_policy** (Optional[NeighborhoodPolicyType]) - Optional - Policy for neighborhood selection. - **seed** (Optional[int]) - Optional - Seed for random number generation. - **op_coupling** (Optional[ndarray]) - Optional - A 2D boolean matrix indicating coupling between destroy and repair operators. - **kwargs** - Additional keyword arguments to be passed to the MAB algorithm. ``` -------------------------------- ### Setting Up ALNS Search Parameters Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Configures parameters for the ALNS search, including the number of iterations, the selection strategy (AlphaUCB), and the stopping criterion (MaxIterations). ```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) ``` -------------------------------- ### Generate Initial Solution for RCPSP (Python) Source: https://alns.readthedocs.io/en/latest/examples/resource_constrained_project_scheduling_problem This snippet demonstrates how to create an initial feasible solution for the RCPSP. It initializes a `RcpspState` object with all jobs in their topological order. The objective (makespan) of this initial solution is then printed. ```python init_sol = RcpspState(list(range(instance.num_jobs))) print(f"Initial solution has objective {init_sol.objective()}.") ``` -------------------------------- ### Autofit SimulatedAnnealing Acceptance Criterion Source: https://alns.readthedocs.io/en/latest/api/accept Returns a SimulatedAnnealing object with an initial temperature automatically set. This initial temperature ensures a specified probability of accepting solutions that are worse than the current solution by a certain percentage. The step size is then calculated to reach a temperature of 1 within a given number of iterations. ```python SimulatedAnnealing._autofit(_init_obj : float_, _worse : float_, _accept_prob : float_, _num_iters : int_, _method : str = 'exponential'_) ``` -------------------------------- ### Initialize and Evaluate Initial Solution (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/cutting_stock_problem.ipynb Initializes a CSP state, generates an initial solution using a greedy insertion heuristic, and prints its objective value. This sets a baseline for the ALNS heuristic. ```python state = CspState([], BEAMS.copy()) init_sol = greedy_insert(state, rng) print("Initial solution has objective value:", init_sol.objective()) ``` -------------------------------- ### Path Removal Destroy Operator in ALNS Source: https://alns.readthedocs.io/en/latest/examples/travelling_salesman_problem Implements the path removal strategy by removing a consecutive sub-path of edges. It randomly selects a starting node and removes a specified number of contiguous edges. ```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 ``` -------------------------------- ### Remove Beams with Most Wastage (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/cutting_stock_problem.ipynb Implements a worst removal destroy operator. It removes beam assignments starting with those that have the highest wastage. Assignments are sorted by wastage in descending order before removal. ```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 ``` -------------------------------- ### SegmentedRouletteWheel Initialization and Parameters Source: https://alns.readthedocs.io/en/latest/api/select Initializes the SegmentedRouletteWheel, which manages operator selection weights over segments. It takes scores, decay rate, segment length, number of destroy and repair operators, and an optional operator coupling matrix. ```python class SegmentedRouletteWheel: def __init__(self, scores: List[float], decay: float, seg_length: int, num_destroy: int, num_repair: int, op_coupling: Optional[ndarray] = None): # ... initialization logic ... pass ``` -------------------------------- ### Segment Removal Operator for RCPSP Source: https://alns.readthedocs.io/en/latest/_sources/examples/resource_constrained_project_scheduling_problem.ipynb Removes a contiguous segment of Q jobs from the current RCPSP solution. It selects a random starting offset and removes the jobs within that segment. This operator is simpler than mobility-based removals. ```python def segment_removal(state, rng): """ Removes a whole segment of jobs from the current solution. """ state = copy.copy(state) offset = rng.integers(1, instance.num_jobs - Q) del state.jobs[offset : offset + Q] return state ``` -------------------------------- ### Generate Initial Solution using Greedy Insert Source: https://alns.readthedocs.io/en/latest/examples/cutting_stock_problem Generates an initial solution for the CSP using the 'greedy_insert' repair operator. It initializes the state with empty assignments and a copy of the beams, then applies the operator. The objective value of the initial solution is printed. ```python rng = rnd.default_rng(SEED) state = CspState([], BEAMS.copy()) init_sol = greedy_insert(state, rng) print("Initial solution has objective value:", init_sol.objective()) ``` -------------------------------- ### Running ALNS Iteration Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Executes the ALNS search process using the configured initial solution, selection strategy, acceptance criteria, and stopping condition. ```python result = alns.iterate(init, select, accept, stop) ``` -------------------------------- ### Apply RecordToRecordTravel Acceptance Criterion in Python Source: https://alns.readthedocs.io/en/latest/_sources/examples/alns_features.ipynb Configures and applies the RecordToRecordTravel acceptance criterion for ALNS. It sets the start threshold, end threshold, step value, and method (linear or exponential) for updating the threshold. The ALNS iteration is then performed with this criterion. ```python accept = RecordToRecordTravel( start_threshold=255, end_threshold=5, step=250 / 10_000, method="linear" ) alns = make_alns() res = alns.iterate(init_sol, select, accept, MaxIterations(10_000)) print(f"Found solution with objective {-res.best_state.objective()}.") ``` -------------------------------- ### Run ALNS Iteration and Collect Results (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/cutting_stock_problem.ipynb Configures the ALNS algorithm's selection, acceptance, and stopping criteria. It then runs the ALNS iteration process starting from the initial solution and stores the best found solution and its objective value. ```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() ``` -------------------------------- ### Run ALNS with LinGreedy Contextual Bandit Policy in Python Source: https://alns.readthedocs.io/en/latest/examples/alns_features This code demonstrates how to initialize and run the ALNS algorithm using the LinGreedy contextual bandit policy. It sets up an MABSelector with specific scores and learning policy, then iterates the ALNS process. ```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()}.") ``` -------------------------------- ### Initialize ALNS Algorithm (Python) Source: https://alns.readthedocs.io/en/latest/examples/resource_constrained_project_scheduling_problem This code initializes the Adaptive Large Neighborhood Search (ALNS) algorithm with a random number generator seeded for reproducibility. It then proceeds to add various 'destroy' operators (like `most_mobile_removal`, `non_peak_removal`, `segment_removal`) and a 'repair' operator (`random_insert`) to the ALNS instance. ```python rng = rnd.default_rng(SEED) alns = ALNS(rng) alns.add_destroy_operator(most_mobile_removal) alns.add_destroy_operator(non_peak_removal) alns.add_destroy_operator(segment_removal) alns.add_repair_operator(random_insert) ``` -------------------------------- ### Adjacent Job Removal Operator Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Implements a destroy operator that removes a specified number of adjacent jobs from the schedule. It randomly selects a starting point in the schedule and removes the subsequent jobs. The removed jobs are added to the 'unassigned' list of the new solution. ```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 ``` -------------------------------- ### Implement SegmentedRouletteWheel Operator Selection in Python Source: https://alns.readthedocs.io/en/latest/examples/alns_features The SegmentedRouletteWheel scheme addresses the limitation of RouletteWheel by fixing operator weights for a segment length. It tracks scores separately for each operator and updates weights after each segment using a decay rate. This Python code shows its setup and integration into ALNS. ```python select = SegmentedRouletteWheel( scores=[5, 2, 1, 0.5], decay=0.8, seg_length=500, num_destroy=2, num_repair=1, ) alns = make_alns() res = alns.iterate(init_sol, select, accept, MaxIterations(10_000)) print(f"Found solution with objective {-res.best_state.objective()}.") ``` -------------------------------- ### Python: Configuring ALNS with Custom Repair Operator Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Sets up an ALNS instance by adding destroy operators (random_removal, adjacent_removal) and the custom greedy_repair_then_local_search operator. This configuration prepares the ALNS for solving optimization problems. ```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) ``` -------------------------------- ### ALNS Class Initialization and Usage Source: https://alns.readthedocs.io/en/latest/api/alns Demonstrates the initialization of the ALNS class with an optional random number generator and its core methods for adding operators and running the search. The 'iterate' method requires an initial solution, operator selection scheme, acceptance criterion, and stopping criterion. ```python from alns import ALNS from alns.utils import read_solution, write_solution from alns.acceptance import Metropolis from alns.operator import OperatorSelectionScheme from alns.stop import StoppingCriterion # Initialize ALNS with a random number generator alns_instance = ALNS(rng=None) # Use default if None # Add destroy and repair operators (assuming 'destroy_op' and 'repair_op' are defined) alns_instance.add_destroy_operator(destroy_op) alns_instance.add_repair_operator(repair_op) # Define necessary components for iteration initial_solution = read_solution('initial_solution.txt') op_select = OperatorSelectionScheme() accept = Metropolis() stop = StoppingCriterion() # Run the ALNS heuristic result = alns_instance.iterate(initial_solution, op_select, accept, stop) # Access the final solution and statistics final_solution = result.best_solution statistics = result.statistics ``` -------------------------------- ### Most Mobile Removal Operator (Python) Source: https://alns.readthedocs.io/en/latest/examples/resource_constrained_project_scheduling_problem Removes jobs that are most mobile within the schedule, defined by their earliest possible start and latest possible finish times. This operator helps in exploring solutions where jobs have flexibility. It depends on the state of the schedule and a random number generator. ```python import copy import numpy as np 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 ``` -------------------------------- ### Configure ALNS for TSP (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/travelling_salesman_problem.ipynb Initializes the ALNS heuristic and adds destroy and repair operators. It includes random removal, path removal, and worst removal as destroy operators, and greedy repair as a repair operator. This setup defines the search space exploration strategy. ```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) ``` -------------------------------- ### Define knapsack problem parameters Source: https://alns.readthedocs.io/en/latest/examples/alns_features Sets up the parameters for a toy 0/1 knapsack problem, including the number of items, their profits and weights, and the maximum capacity of the knapsack. It also defines the rate at which items are removed. ```python n = 100 p = np.random.randint(1, 100, size=n) w = np.random.randint(10, 50, size=n) W = 1_000 # Percentage of items to remove in each iteration destroy_rate = 0.25 ``` -------------------------------- ### Implement Greedy Randomised Adaptive Search Procedure (GRASP) with ALNS Source: https://alns.readthedocs.io/en/latest/_sources/examples/other%20single-trajectory%20heuristics.rst Illustrates GRASP implementation with ALNS. It involves a destroy operator to break the solution (potentially completely) and a greedy randomised repair operator. The RouletteWheel selection scheme is suggested. ```python from alns import ALNS, State def destroy(sol: State, rng) -> State: return def greedy_randomised_repair(sol: State, rng) -> State: return alns = ALNS() alns.add_destroy_operator(destroy) alns.add_repair_operator(greedy_randomised_repair) ``` -------------------------------- ### Adjacent Job Removal Operator (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/permutation_flow_shop_problem.ipynb Implements an adjacent job removal operator. It randomly selects a starting point in the schedule and removes a specified number of contiguous jobs. These removed jobs are then added to the unassigned list. This operator is often used in metaheuristics to explore the solution space. ```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 ``` -------------------------------- ### Evaluate and Print Initial Solution (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/permutation_flow_shop_problem.ipynb Generates an initial solution using the NEH algorithm and evaluates its objective value. It then calculates and prints the percentage difference between the obtained objective value and the best known value (BKV). This snippet demonstrates how to use the NEH function and assess its performance. ```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}.") ``` -------------------------------- ### Plotting Initial Solution Schedule Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Generates a plot for the schedule of the initial solution obtained from the NEH algorithm. ```python plot(init.schedule, "NEH") ``` -------------------------------- ### Load and Parse PFSP Instance Data from File Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Defines a dataclass to hold PFSP instance data and a class method to load this data from a file. It parses the number of jobs, machines, best known value, and processing times. ```python @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") ``` -------------------------------- ### Nearest Neighbor Heuristic for Initial CVRP Solution Source: https://alns.readthedocs.io/en/latest/_sources/examples/capacitated_vehicle_routing_problem.ipynb Generates an initial feasible solution for the CVRP using a nearest neighbor heuristic. It iteratively builds routes by adding the closest unvisited customer until vehicle capacity is reached, then starts a new route. This function returns a CvrpState object representing the initial solution. ```python def neighbors(customer): """ Return the nearest neighbors of the customer, excluding the depot. """ locations = np.argsort(data["edge_weight"][customer]) return locations[locations != 0] def nearest_neighbor(): """ Build a solution by iteratively constructing routes, where the nearest customer is added until the route has met the vehicle capacity limit. """ routes = [] unvisited = set(range(1, data["dimension"])) while unvisited: route = [0] # Start at the depot route_demands = 0 while unvisited: # Add the nearest unvisited customer to the route till max capacity current = route[-1] nearest = [nb for nb in neighbors(current) if nb in unvisited][0] if route_demands + data["demand"][nearest] > data["capacity"]: break route.append(nearest) unvisited.remove(nearest) route_demands += data["demand"][nearest] customers = route[1:] # Remove the depot routes.append(customers) return CvrpState(routes) ``` -------------------------------- ### Compute Serial Schedule for RCPSP Source: https://alns.readthedocs.io/en/latest/examples/resource_constrained_project_scheduling_problem Calculates a serial schedule for a given set of jobs, returning the schedule and resource usage. This function implements the algorithm described in Fleszar and Hindi (2004). It requires an 'instance' object with job durations, predecessors, needs, resources, and the last job's identifier. The output is a tuple containing the schedule (start times for each job) and a 2D NumPy array of resource usage over time. ```python @lru_cache(32) def schedule(jobs: tuple[int]) -> tuple[np.ndarray, np.ndarray]: """ Computes a serial schedule of the given list of jobs. See Figure 1 in Fleszar and Hindi (2004) for the algorithm. Returns the schedule, and the resources used. Fleszar, K. and K.S. Hindi. 2004. Solving the resource-constrained project scheduling problem by a variable neighbourhood search. _European Journal of Operational Research_. 155 (2): 402 -- 413. """ used = np.zeros((instance.duration.sum(), instance.num_resources)) sched = np.zeros(instance.num_jobs, dtype=int) for job in jobs: pred = instance.predecessors[job] t = max(sched[pred] + instance.duration[pred], default=0) needs = instance.needs[job] duration = instance.duration[job] # This efficiently determines the first feasible insertion point # after t. We compute whether resources are available, and add the # offset s of the first time sufficient are available for the # duration of the job. res_ok = np.all(used[t:] + needs <= instance.resources, axis=1) for s in np.flatnonzero(res_ok): if np.all(res_ok[s : s + duration]): sched[job] = t + s used[t + s : t + s + duration] += needs break return sched, used[: sched[instance.last_job]] ``` -------------------------------- ### Compute All Insertion Costs with Taillard's Acceleration Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Calculates the makespan for inserting a job at every possible position in a given schedule using Taillard's acceleration method. This function has a time complexity of O(nm) and returns a list of tuples, where each tuple contains an insertion index and the resulting makespan. ```python 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): 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)] ``` -------------------------------- ### Python: Running ALNS Iteration with Specific Parameters Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Executes the ALNS iteration process using defined parameters for selection, acceptance, and stopping criteria. It configures the number of iterations and uses specific strategies like AlphaUCB and Simulated Annealing. ```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) result = alns.iterate(init, select, accept, stop) ``` -------------------------------- ### Setting Callback Functions for ALNS Events Source: https://alns.readthedocs.io/en/latest/api/alns Illustrates how to register callback functions for specific events during the ALNS execution: when a new global best solution is found ('on_best'), when a better solution than the incumbent is found ('on_better'), when a solution is accepted ('on_accept'), and when a solution is rejected ('on_reject'). Callbacks receive the candidate state and generator, and modifications should be done in-place. ```python def on_best_callback(candidate_state, rng): print(f"New best solution found: {candidate_state}") def on_better_callback(candidate_state, rng): # Modify candidate_state in-place if needed pass def on_accept_callback(candidate_state, rng): pass def on_reject_callback(candidate_state, rng): pass alns_instance = ALNS() alns_instance.on_best(on_best_callback) alns_instance.on_better(on_better_callback) alns_instance.on_accept(on_accept_callback) alns_instance.on_reject(on_reject_callback) ``` -------------------------------- ### Initialize ALNS with Destroy and Repair Operators (Python) Source: https://alns.readthedocs.io/en/latest/_sources/examples/permutation_flow_shop_problem.ipynb This snippet shows how to initialize an ALNS object and add different types of destroy and repair operators. It demonstrates the flexibility of ALNS in incorporating various neighborhood exploration strategies. ```python alns = ALNS(rnd.default_rng(SEED)) alns.add_destroy_operator(random_removal) ``` ```python alns.add_destroy_operator(adjacent_removal) ``` ```python alns.add_repair_operator(greedy_repair) ``` -------------------------------- ### Evaluating Initial NEH Solution Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Calculates and prints the objective value of the initial solution generated by the NEH algorithm and its percentage difference from the best known value (bkv). ```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}.") ``` -------------------------------- ### Plot Gantt Chart for PFSP Schedule Source: https://alns.readthedocs.io/en/latest/examples/permutation_flow_shop_problem Generates and displays a Gantt chart visualizing the schedule of jobs on machines. It uses job completion times and processing times to represent job durations and positions. ```python 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() ```