| 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: | André Leite [aut, cre] (ORCID: <https://orcid.org/0000-0002-4718-9766>), Marcos Wasiliew [aut] (ORCID: <https://orcid.org/0009-0004-4694-3159>), Hugo Vasconcelos [aut] (ORCID: <https://orcid.org/0000-0001-6249-0920>), Carlos Amorim [aut] (ORCID: <https://orcid.org/0000-0001-6315-8305>), Diogo Bezerra [aut] (ORCID: <https://orcid.org/0000-0002-1216-8674>), Júlia Nascimento Barreto [aut] (ORCID: <https://orcid.org/0009-0004-2851-7770>), 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: | André Leite <leite@castlab.org> |
| License: | MIT + file LICENSE |
| Version: | 0.2.2 |
| Built: | 2026-10-07 09:07:45 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.
A shipment is a paired pickup and delivery: the same vehicle must pick up the goods at the pickup point and drop them off at the delivery point, with the pickup happening first, in the same trip. This models the classic pickup-and-delivery problem (PDPTW when combined with time windows).
add_shipments(model, data)add_shipments(model, data)
model |
A |
data |
A tibble/data.frame with at least the columns |
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 = 50L, min_perturbations = 1L, max_perturbations = 25L, history_length = 300L, num_iters_no_improvement = 150000L, exhaustive_on_best = TRUE )ils_params( num_neighbours = 50L, min_perturbations = 1L, max_perturbations = 25L, history_length = 300L, num_iters_no_improvement = 150000L, exhaustive_on_best = TRUE )
num_neighbours |
Granular neighbourhood size (activities per neighbourhood). Default 50, as in PyVRP. |
min_perturbations, max_perturbations
|
Range of perturbations per iteration. |
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 client, pickup or delivery visit:
route_id, depot (start depot, 1-based), position, activity
("client", "pickup" or "delivery"), client (client number, NA
for shipment visits), shipment (shipment number, NA for client
visits), trip, 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; trip in multi-trip routes.
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.
Optional clients and shipments that are not part of any route (prize-collecting problems).
unplanned(x, ...)unplanned(x, ...)
x |
A |
... |
Unused. |
A tibble with columns activity ("client" or "pickup"/
"delivery") and index (1-based client or shipment number).
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 vrpr's convention: depots first (low indices), then clients, then one
pickup and one delivery location per shipment.
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.