| Title: | Vehicle Routing Problem Solver Built on 'PyVRP' |
|---|---|
| Description: | A 'tidyverse'-style interface to high-performance vehicle routing problem (VRP) solving. Vendors the C++ core of the 'PyVRP' solver (<https://github.com/PyVRP/PyVRP>) and rewires it through 'cpp11', with no 'Python' runtime dependency. Supports the capacitated VRP, time windows, multiple depots, heterogeneous fleets, prize-collecting and multi-trip variants, driven by an iterated local search metaheuristic. |
| Authors: | Andre Leite [aut, cre], Marcos Wasilew [aut], Hugo Vasconcelos [aut], Carlos Amorim [aut], Diogo Bezerra [aut], Niels Wouda [ctb, cph] (author of the vendored PyVRP C++ core), Thibaut Vidal [cph] (HGS-CVRP, parts of the vendored C++ core), ORTEC [cph] (HGS-DIMACS, parts of the vendored C++ core) |
| Maintainer: | Andre Leite <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-07-05 09:46:10 UTC |
| Source: | https://github.com/strategicprojects/vrpr |
Defines a group from which at most one client is visited (or exactly one if
required = TRUE). Useful for prize-collecting with exclusive alternatives
(e.g. serving one of several equivalent points). Clients in the group
automatically become optional (individual required = FALSE); use prize to
encourage a visit.
add_client_group(model, clients, required = FALSE)add_client_group(model, clients, required = FALSE)
model |
A |
clients |
Vector of client numbers (1-based, in the order of
|
required |
If |
The updated vrpr_model.
Add clients to the model
add_clients(model, data)add_clients(model, data)
model |
A |
data |
A tibble/data.frame with at least the columns |
The updated vrpr_model.
Add a depot to the model
add_depot(model, x, y, tw_early = 0, tw_late = Inf, service = 0)add_depot(model, x, y, tw_early = 0, tw_late = Inf, service = 0)
model |
A |
x, y
|
Depot coordinates. |
tw_early, tw_late
|
Depot time window (opening/closing). |
service |
Service time at the depot (e.g. loading), per trip. |
The updated vrpr_model.
Add a vehicle type to the model
add_vehicle_type( model, num_available, capacity, fixed_cost = 0, tw_early = 0, tw_late = Inf, max_duration = Inf, unit_distance_cost = 1, unit_duration_cost = 0, depot = 1L, start_depot = depot, end_depot = depot, reload_depots = integer(0), max_reloads = Inf )add_vehicle_type( model, num_available, capacity, fixed_cost = 0, tw_early = 0, tw_late = Inf, max_duration = Inf, unit_distance_cost = 1, unit_duration_cost = 0, depot = 1L, start_depot = depot, end_depot = depot, reload_depots = integer(0), max_reloads = Inf )
model |
A |
num_available |
Number of vehicles available of this type. |
capacity |
Vehicle capacity. |
fixed_cost |
Fixed cost per vehicle used. |
tw_early, tw_late
|
Vehicle shift time window (start/end). |
max_duration |
Maximum route duration. |
unit_distance_cost, unit_duration_cost
|
Variable cost per unit of
distance and of duration for this type. Varying these (and |
depot |
Index (1-based) of the depot vehicles of this type start from and
return to. Shortcut to set |
start_depot, end_depot
|
Indices (1-based) of the start and end depots, in
the order of |
reload_depots |
Indices (1-based) of depots where vehicles of this type may reload/empty mid-route, enabling multi-trip routes. Empty (default) = no reloading. |
max_reloads |
Maximum number of reloads per route. |
Call add_vehicle_type() several times for a fleet with multiple
vehicle types (different capacities, costs, shifts or depots).
The updated vrpr_model.
Cost of a result or solution
cost(x, ...)cost(x, ...)
x |
A |
... |
Unused. |
The objective cost (a numeric scalar); Inf if no feasible solution
was found.
ILS solver parameters
ils_params( num_neighbours = 20L, min_perturbations = 1L, max_perturbations = 25L, init_load = 20, init_tw = 6, init_dist = 6, history_length = 300L, num_iters_no_improvement = 150000L, exhaustive_on_best = TRUE )ils_params( num_neighbours = 20L, min_perturbations = 1L, max_perturbations = 25L, init_load = 20, init_tw = 6, init_dist = 6, history_length = 300L, num_iters_no_improvement = 150000L, exhaustive_on_best = TRUE )
num_neighbours |
Granular neighbourhood size (k neighbours per client). |
min_perturbations, max_perturbations
|
Range of perturbations per iteration. |
init_load, init_tw, init_dist
|
Initial penalties. |
history_length |
Length of the late-acceptance history (> 0). Default 300, as in PyVRP. |
num_iters_no_improvement |
Iterations without improvement before restarting from the best. |
exhaustive_on_best |
Refine each new best with an exhaustive search? |
A list of parameters.
Plot a VRP model (depots and clients only)
## S3 method for class 'vrpr_model' plot(x, ...)## S3 method for class 'vrpr_model' plot(x, ...)
x |
A |
... |
Unused. |
A ggplot object.
Draws depots, clients and the routes (one colour per route) over the instance coordinates. Unvisited optional clients (prize-collecting) appear as hollow circles.
## S3 method for class 'vrpr_result' plot(x, show_clients = TRUE, ...)## S3 method for class 'vrpr_result' plot(x, show_clients = TRUE, ...)
x |
A |
show_clients |
Reserved; clients are always drawn. |
... |
Unused. |
A ggplot object.
Reads VRPTW instances in Solomon (and Gehring-Homberger) format, with the
VEHICLE section (number and capacity) and the CUSTOMER table (coordinates,
demand, time window and service time). Customer 0 is the depot.
read_solomon(path, num_vehicles = NULL)read_solomon(path, num_vehicles = NULL)
path |
Path to the file. |
num_vehicles |
Number of vehicles; if |
A vrp_model() ready for vrp_solve().
Reads CVRP (and VRPTW) instances in VRPLIB/CVRPLIB format (extended TSPLIB),
such as the X set by Uchoa et al. Supports Euclidean coordinates
(EDGE_WEIGHT_TYPE : EUC_2D); time-window and service-time sections are read
when present.
read_vrplib(path, num_vehicles = NULL)read_vrplib(path, num_vehicles = NULL)
path |
Path to the |
num_vehicles |
Number of available vehicles. If |
A vrp_model() ready for vrp_solve().
Routes of a solution, in long (tidy) format
routes(x, ...)routes(x, ...)
x |
|
... |
Unused. |
A tibble with one row per visit: route_id, depot (start depot,
1-based), position, client, vehicle_type, start_service (start of
service) and wait (waiting time). The last two are only meaningful with
time windows (VRPTW); depot varies in the MDVRP.
Cost of a solution
solution_cost(solution, cost_evaluator = NULL)solution_cost(solution, cost_evaluator = NULL)
solution |
|
cost_evaluator |
A |
The penalised cost (a numeric scalar). For feasible solutions this
is the objective cost; the feasible attribute reports feasibility.
One-row summary of a result (tibble)
## S3 method for class 'vrpr_result' summary(object, ...)## S3 method for class 'vrpr_result' summary(object, ...)
object |
A |
... |
Unused. |
A one-row tibble with cost, feasibility, number of routes, iterations and runtime.
In prize-collecting problems, clients with required = FALSE may be left out
if the prize does not offset the routing cost.
unvisited_clients(x, ...)unvisited_clients(x, ...)
x |
A |
... |
Unused. |
An integer vector of the (1-based) client numbers not visited.
Creates a penalised-cost evaluator. Penalties multiply constraint violations (load, time window, maximum distance) to form the smoothed cost the solver minimises. For a feasible solution, the penalised cost equals the objective cost.
vrp_cost_evaluator(load_penalties = 1, tw_penalty = 1, dist_penalty = 1)vrp_cost_evaluator(load_penalties = 1, tw_penalty = 1, dist_penalty = 1)
load_penalties |
Penalty per unit of excess load, per load dimension. A scalar is recycled across all dimensions. |
tw_penalty |
Penalty per unit of time warp (time-window violation). |
dist_penalty |
Penalty per unit of distance above the maximum. |
A vrpr_cost_evaluator object.
vrp_model() creates an empty model to which depots, clients and vehicle
types are added via the pipe (|>). It is the tidy equivalent of PyVRP's
Model class – the data boundary uses tibbles, not one object at a time.
vrp_model()vrp_model()
A vrpr_model object.
clients <- tibble::tibble( x = c(10, 25, 40), y = c(5, 30, 12), demand = c(10, 15, 8) ) m <- vrp_model() |> add_depot(x = 0, y = 0) |> add_clients(clients) |> add_vehicle_type(num_available = 5, capacity = 100) mclients <- tibble::tibble( x = c(10, 25, 40), y = c(5, 30, 12), demand = c(10, 15, 8) ) m <- vrp_model() |> add_depot(x = 0, y = 0) |> add_clients(clients) |> add_vehicle_type(num_available = 5, capacity = 100) m
Builds PyVRP's C++ ProblemData structure from a vrp_model(). Locations
follow PyVRP's convention: depots first (low indices), then clients.
vrp_problem_data(model, distance = NULL, duration = NULL)vrp_problem_data(model, distance = NULL, duration = NULL)
model |
A |
distance, duration
|
Matrices ( |
Integer measures (distance, duration, cost, load) travel as R numeric with
integer semantics; non-integer values are rejected at the C++ boundary. Use
Inf for "unconstrained" limits (e.g. tw_late).
A vrpr_problem_data object (a wrapper around a C++ external pointer).
Generate a random solution
vrp_random_solution(problem_data, seed = 42L)vrp_random_solution(problem_data, seed = 42L)
problem_data |
|
seed |
Integer seed. |
A vrpr_solution object.
Build a solution from explicit routes
vrp_solution(problem_data, routes)vrp_solution(problem_data, routes)
problem_data |
|
routes |
A list of integer vectors; each vector is a route given as client numbers (1..n_clients), in visit order. All routes use the first vehicle type. |
A vrpr_solution object.
Runs the iterated local search (ILS) solver on a model, using PyVRP's vendored C++ core.
vrp_solve(model, stop, seed = 42L, params = ils_params(), display = TRUE)vrp_solve(model, stop, seed = 42L, params = ils_params(), display = TRUE)
model |
A |
stop |
A stopping criterion (see vrpr_stop), e.g. |
seed |
Integer seed for reproducibility. |
params |
Solver parameters (see |
display |
Show progress via |
A vrpr_result object with the best solution, cost, routes and run
statistics. Use cost(), routes() and summary() to inspect it.
clients <- tibble::tibble( x = c(10, 25, 40, 15), y = c(5, 30, 12, 22), demand = c(10, 15, 8, 12) ) res <- vrp_model() |> add_depot(x = 0, y = 0) |> add_clients(clients) |> add_vehicle_type(num_available = 3, capacity = 50) |> vrp_solve(stop = max_iterations(200), display = FALSE) cost(res) routes(res)clients <- tibble::tibble( x = c(10, 25, 40, 15), y = c(5, 30, 12, 22), demand = c(10, 15, 8, 12) ) res <- vrp_model() |> add_depot(x = 0, y = 0) |> add_clients(clients) |> add_vehicle_type(num_available = 3, capacity = 50) |> vrp_solve(stop = max_iterations(200), display = FALSE) cost(res) routes(res)
Control when the iterated local search loop should terminate. Each function
returns a callable object (closure) that the solver invokes every iteration,
receiving the cost of the current best solution and returning TRUE to stop.
max_runtime(seconds) max_iterations(max_iters) no_improvement(n) first_feasible()max_runtime(seconds) max_iterations(max_iters) no_improvement(n) first_feasible()
seconds |
Maximum run time, in seconds. |
max_iters |
Maximum number of iterations. |
n |
Number of consecutive iterations without improvement before stopping. |
These are the R equivalent of PyVRP's pyvrp.stop module.
An object of class vrpr_stop: a function
function(best_cost, feasible) returning TRUE/FALSE.