ExVrp.Solver (ExVrp v0.4.2)

Copy Markdown View Source

Main solver interface for VRP problems.

This module provides the solve/2 function which is a direct port of PyVRP's solve() function. It sets up the solver components and runs Iterated Local Search with Late Acceptance Hill-Climbing.

Summary

Functions

Solves a VRP model using Iterated Local Search.

Types

solve_opts()

@type solve_opts() :: [
  max_iterations: pos_integer(),
  max_runtime: pos_integer(),
  stop: ExVrp.StoppingCriteria.t(),
  seed: non_neg_integer(),
  num_starts: pos_integer() | :auto,
  penalty_params: ExVrp.PenaltyManager.Params.t(),
  ils_params: ExVrp.IteratedLocalSearch.Params.t(),
  on_progress: (map() -> any()) | nil
]

Functions

solve(model, opts \\ [])

@spec solve(ExVrp.Model.t(), solve_opts()) ::
  {:ok, ExVrp.IteratedLocalSearch.Result.t()} | {:error, term()}

Solves a VRP model using Iterated Local Search.

This is a port of PyVRP's solve() function. It:

  1. Creates the problem data from the model
  2. Initializes the PenaltyManager for dynamic penalty adjustment
  3. Creates an initial solution using local search on empty solution
  4. Runs Iterated Local Search until stopping criterion is met

Options

  • :max_iterations - Maximum number of iterations (default: 10_000)
  • :max_runtime - Maximum runtime in seconds (default: unlimited). Matches PyVRP.
  • :stop - Custom StoppingCriteria (overrides max_iterations/max_runtime)
  • :seed - Random seed for reproducibility (default: random)
  • :num_starts - Number of parallel independent solver starts (default: :auto). Each start uses a different seed and runs its own ILS chain. The best result across all starts is returned. Use :auto to pick based on available cores (div(schedulers_online, 2)).
  • :penalty_params - PenaltyManager.Params for penalty adjustment
  • :ils_params - IteratedLocalSearch.Params for ILS behavior
  • :on_progress - Optional callback function receiving progress maps during ILS iterations (time-gated at ~1s intervals). When num_starts > 1, progress maps include :seed_idx and :seed fields.

Returns

  • {:ok, result} - Successfully found a solution. Result has:
    • result.best - Best Solution found
    • result.cost() - Cost of best solution (infinity if infeasible)
    • result.feasible?() - Whether solution is feasible
    • result.num_iterations - Total iterations
    • result.runtime - Runtime in milliseconds
  • {:error, reason} - Failed to solve

Example

model = Model.new()
|> Model.add_depot(x: 0, y: 0)
|> Model.add_vehicle_type(num_available: 2, capacity: [100])
|> Model.add_client(x: 10, y: 0, delivery: [20])

{:ok, result} = Solver.solve(model, max_iterations: 1000)
IO.puts("Best distance: #{result.best.distance}")

# With time limit (seconds, like PyVRP)
{:ok, result} = Solver.solve(model, max_runtime: 60.0)