---
title: "Getting started with vrpr"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Getting started with vrpr}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r}
#| include: false
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 6,
  fig.height = 4.2,
  fig.align = "center"
)
```

`vrpr` is a tidyverse-style interface to the [PyVRP](https://github.com/PyVRP/PyVRP)
vehicle-routing solver. You build a model by piping together depots, clients and
vehicle types, then call `vrp_solve()`. The heavy lifting runs in PyVRP's C++ core
(rewired with cpp11), so there is **no Python dependency**.

```{r}
#| label: setup
library(vrpr)
```

## A first CVRP

The capacitated VRP (CVRP) is the base case: clients have a `demand`, vehicles a
`capacity`, and we minimise total distance. The data boundary is a tibble.

```{r}
#| label: cvrp
set.seed(1)
clients <- tibble::tibble(
  x = round(runif(20, -50, 50)),
  y = round(runif(20, -50, 50)),
  demand = sample(5:15, 20, replace = TRUE)
)

model <- vrp_model() |>
  add_depot(x = 0, y = 0) |>
  add_clients(clients) |>
  add_vehicle_type(num_available = 5, capacity = 50)

res <- vrp_solve(model, stop = max_iterations(500), seed = 1, display = FALSE)
res
```

Inspect the result with `cost()`, `routes()` (a tidy long table) and `summary()`:

```{r}
#| label: inspect
cost(res)
head(routes(res))
summary(res)
```

If `{ggplot2}` is installed, `plot()` draws the routes:

```{r}
#| label: plot-cvrp
#| eval: !expr requireNamespace("ggplot2", quietly = TRUE)
plot(res)
```

## Stopping criteria

`vrp_solve()` runs until a stopping criterion fires. Combine time- and
iteration-based limits as needed:

```{r}
#| label: stops
#| eval: false
vrp_solve(model, stop = max_runtime(seconds = 10)) # wall-clock budget
vrp_solve(model, stop = max_iterations(5000))      # iteration budget
vrp_solve(model, stop = no_improvement(1000))      # stop when stuck
```

## Time windows (VRPTW)

Add `tw_early`, `tw_late` and `service` columns to the clients to turn the model
into a VRP with time windows. The solver respects the windows, and `routes()`
reports the `start_service` and `wait` time of each visit.

```{r}
#| label: vrptw
tw_clients <- tibble::tibble(
  x        = c(10, 20, 30, 40, 50, 60),
  y        = 0,
  demand   = 10,
  tw_early = c(0, 30, 60, 90, 120, 150),
  tw_late  = c(50, 80, 110, 140, 170, 200),
  service  = 10
)

vrptw <- vrp_model() |>
  add_depot(0, 0, tw_early = 0, tw_late = 500) |>
  add_clients(tw_clients) |>
  add_vehicle_type(num_available = 2, capacity = 60, tw_early = 0, tw_late = 500)

res_tw <- vrp_solve(vrptw, stop = max_iterations(500), seed = 1, display = FALSE)
routes(res_tw)[, c("route_id", "client", "start_service", "wait")]
```

## Heterogeneous fleet

Call `add_vehicle_type()` several times for a fleet of different vehicles. Here a
cheap type and an expensive one share the same capacity; the solver prefers the
cheaper type and only uses what it needs.

```{r}
#| label: heterogeneous
het <- vrp_model() |>
  add_depot(0, 0) |>
  add_clients(clients) |>
  add_vehicle_type(num_available = 3, capacity = 50, unit_distance_cost = 1) |>
  add_vehicle_type(num_available = 3, capacity = 50, unit_distance_cost = 5)

res_het <- vrp_solve(het, stop = max_iterations(500), seed = 1, display = FALSE)
table(routes(res_het)$vehicle_type)
```

## Multiple depots (MDVRP)

Add several depots and base each vehicle type at one of them with
`add_vehicle_type(depot = i)`. The `routes()` output gains a `depot` column.

```{r}
#| label: mdvrp
mdvrp <- vrp_model() |>
  add_depot(x = -50, y = 0) |>
  add_depot(x =  50, y = 0) |>
  add_clients(tibble::tibble(
    x = c(-55, -45, -50, 55, 45, 50),
    y = c(5, -5, 10, 5, -5, 8),
    demand = 10
  )) |>
  add_vehicle_type(num_available = 3, capacity = 50, depot = 1) |>
  add_vehicle_type(num_available = 3, capacity = 50, depot = 2)

res_md <- vrp_solve(mdvrp, stop = max_iterations(500), seed = 1, display = FALSE)
routes(res_md)[, c("route_id", "depot", "client")]
```

## Prize-collecting

Mark clients as optional with `required = FALSE` and give them a `prize`. The
solver visits an optional client only when the prize offsets the routing cost;
`unvisited_clients()` lists those left out. `add_client_group()` defines mutually
exclusive alternatives.

```{r}
#| label: prize
pc <- vrp_model() |>
  add_depot(0, 0) |>
  add_clients(tibble::tibble(
    x = c(5, -5, 0, 100, 100),
    y = c(5, -5, 8, 10, -10),
    demand = 10,
    required = c(TRUE, TRUE, TRUE, FALSE, FALSE),
    prize = c(0, 0, 0, 5, 500)
  )) |>
  add_vehicle_type(num_available = 4, capacity = 50)

res_pc <- vrp_solve(pc, stop = max_iterations(500), seed = 1, display = FALSE)
unvisited_clients(res_pc)
```

## Reading standard instances

`read_vrplib()` and `read_solomon()` read CVRP/VRPTW instances in the standard
VRPLIB/TSPLIB and Solomon formats, returning a `vrpr_model` ready to solve.

```{r}
#| label: read
path <- system.file("extdata", "sample-n6-k2.vrp", package = "vrpr")
read_vrplib(path) |>
  vrp_solve(stop = max_iterations(200), seed = 1, display = FALSE) |>
  cost()
```

## Other variants

The same data boundary supports more variants:

- **Pickup & delivery / backhaul** -- add a `pickup` column to clients; the
  collected load counts toward capacity along the route.
- **Multi-trip** -- `add_vehicle_type(reload_depots = i, max_reloads = k)` lets a
  vehicle return to a depot to reload and run several trips.

See `?add_vehicle_type` and `?add_clients` for the full set of options.
