PyVRP
The top-level pyvrp
module exposes several core classes needed to run the VRP solver.
These include the core GeneticAlgorithm
, and the Population
that manages a Solution
pool.
Most classes take parameter objects that allow for advanced configuration - but sensible defaults are also provided.
Finally, after running, the GeneticAlgorithm
returns a Result
object.
This object can be used to obtain the best observed solution, and detailed runtime statistics.
Hint
Have a look at the examples to see how these classes relate!
- class Model
A simple interface for modelling vehicle routing problems with PyVRP.
- Attributes:
locations
Returns all locations (depots and clients) in the current model.
vehicle_types
Returns the vehicle types in the current model.
Methods
add_client
(x, y[, demand, service_duration, ...])Adds a client with the given attributes to the model.
add_depot
(x, y[, tw_early, tw_late])Adds a depot with the given attributes to the model.
add_edge
(frm, to, distance[, duration])Adds an edge \((i, j)\) between
frm
(\(i\)) andto
(\(j\)).add_vehicle_type
(capacity, num_available)Adds a vehicle type with the given number of available vehicles of given capacity to the model.
data
()Creates and returns a
ProblemData
instance from this model's attributes.from_data
(data)Constructs a model instance from the given data.
solve
(stop[, seed])Solve this model.
- add_client(x: int, y: int, demand: int = 0, service_duration: int = 0, tw_early: int = 0, tw_late: int = 0, release_time: int = 0, prize: int = 0, required: bool = True) Client
Adds a client with the given attributes to the model. Returns the created
Client
instance.
- add_depot(x: int, y: int, tw_early: int = 0, tw_late: int = 0) Client
Adds a depot with the given attributes to the model. Returns the created
Client
instance.Warning
PyVRP does not yet support multi-depot VRPs. For now, only one depot can be added to the model.
- add_edge(frm: Client, to: Client, distance: int, duration: int = 0) Edge
Adds an edge \((i, j)\) between
frm
(\(i\)) andto
(\(j\)). The edge can be given distance and duration attributes. Distance is required, but the default duration is zero. Returns the created edge.- Raises:
ValueError
When either distance or duration is a negative value.
- add_vehicle_type(capacity: int, num_available: int) VehicleType
Adds a vehicle type with the given number of available vehicles of given capacity to the model. Returns the created vehicle type.
- Raises:
ValueError
When the number of available vehicles or capacity is not a positive value.
- data() ProblemData
Creates and returns a
ProblemData
instance from this model’s attributes.
- classmethod from_data(data: ProblemData) Model
Constructs a model instance from the given data.
- Parameters:
- data
Problem data to feed into the model.
- Returns:
Model
A model instance representing the given data.
- property locations: List[Client]
Returns all locations (depots and clients) in the current model. The clients in the routes of the solution returned by
solve()
can be used to index these locations.
- solve(stop: StoppingCriterion, seed: int = 0) Result
Solve this model.
- Parameters:
- stop
Stopping criterion to use.
- seed, optional
Seed value to use for the PRNG, by default 0.
- Returns:
Result
The solution result object, containing the best found solution.
- property vehicle_types: List[VehicleType]
Returns the vehicle types in the current model. The routes of the solution returned by
solve()
have a propertyvehicle_type()
that can be used to index these vehicle types.
- class GeneticAlgorithmParams(repair_probability: 'float' = 0.8, collect_statistics: 'bool' = False, intensify_probability: 'float' = 0.15, intensify_on_best: 'bool' = True, nb_iter_no_improvement: 'int' = 20000)
- class GeneticAlgorithm(data: ProblemData, penalty_manager: PenaltyManager, rng: XorShift128, population: Population, local_search: LocalSearch, crossover_op: CrossoverOperator, initial_solutions: Collection[Solution], params: GeneticAlgorithmParams = GeneticAlgorithmParams(repair_probability=0.8, collect_statistics=False, intensify_probability=0.15, intensify_on_best=True, nb_iter_no_improvement=20000))
Creates a GeneticAlgorithm instance.
- Parameters:
- data
Data object describing the problem to be solved.
- penalty_manager
Penalty manager to use.
- rng
Random number generator.
- population
Population to use.
- local_search
Local search instance to use.
- crossover_op
Crossover operator to use for generating offspring.
- initial_solutions
Initial solutions to use to initialise the population.
- params
Genetic algorithm parameters. If not provided, a default will be used.
- Raises:
ValueError
When the population is empty.
Methods
run
(stop)Runs the genetic algorithm with the provided stopping criterion.
- run(stop: StoppingCriterion)
Runs the genetic algorithm with the provided stopping criterion.
- Parameters:
- stop
Stopping criterion to use. The algorithm runs until the first time the stopping criterion returns
True
.
- Returns:
Result
A Result object, containing statistics and the best found solution.
- class PenaltyParams(init_capacity_penalty: int = 20, init_time_warp_penalty: int = 6, repair_booster: int = 12, num_registrations_between_penalty_updates: int = 50, penalty_increase: float = 1.34, penalty_decrease: float = 0.32, target_feasible: float = 0.43)
The penalty manager parameters.
- Parameters:
- init_capacity_penalty
Initial penalty on excess capacity. This is the amount by which one unit of excess load capacity is penalised in the objective, at the start of the search.
- init_time_warp_penalty
Initial penalty on time warp. This is the amount by which one unit of time warp (time window violations) is penalised in the objective, at the start of the search.
- repair_booster
A repair booster value \(r \ge 1\). This value is used to temporarily multiply the current penalty terms, to force feasibility. See also
get_booster_cost_evaluator()
.- num_registrations_between_penalty_updates
Number of feasibility registrations between penalty value updates. The penalty manager updates the penalty terms every once in a while based on recent feasibility registrations. This parameter controls how often such updating occurs.
- penalty_increase
Amount \(p_i \ge 1\) by which the current penalties are increased when insufficient feasible solutions (see
target_feasible
) have been found amongst the most recent registrations. The penalty values \(v\) are updated as \(v \gets p_i v\).- penalty_decrease
Amount \(p_d \in [0, 1]\) by which the current penalties are decreased when sufficient feasible solutions (see
target_feasible
) have been found amongst the most recent registrations. The penalty values \(v\) are updated as \(v \gets p_d v\).- target_feasible
Target percentage \(p_f \in [0, 1]\) of feasible registrations in the last
num_registrations_between_penalty_updates
registrations. This percentage is used to update the penalty terms: when insufficient feasible solutions have been registered, the penalties are increased; similarly, when too many feasible solutions have been registered, the penalty terms are decreased. This ensures a balanced population, with a fraction \(p_f\) feasible and a fraction \(1 - p_f\) infeasible solutions.
- Attributes:
- init_capacity_penalty
Initial penalty on excess capacity.
- init_time_warp_penalty
Initial penalty on time warp.
- repair_booster
A repair booster value.
- num_registrations_between_penalty_updates
Number of feasibility registrations between penalty value updates.
- penalty_increase
Amount \(p_i \ge 1\) by which the current penalties are increased when insufficient feasible solutions (see
target_feasible
) have been found amongst the most recent registrations.- penalty_decrease
Amount \(p_d \in [0, 1]\) by which the current penalties are decreased when sufficient feasible solutions (see
target_feasible
) have been found amongst the most recent registrations.- target_feasible
Target percentage \(p_f \in [0, 1]\) of feasible registrations in the last
num_registrations_between_penalty_updates
registrations.
- class PenaltyManager(params: PenaltyParams = PenaltyParams(init_capacity_penalty=20, init_time_warp_penalty=6, repair_booster=12, num_registrations_between_penalty_updates=50, penalty_increase=1.34, penalty_decrease=0.32, target_feasible=0.43))
Creates a PenaltyManager instance.
This class manages time warp and load penalties, and provides penalty terms for given time warp and load values. It updates these penalties based on recent history, and can be used to provide a temporary penalty booster object that increases the penalties for a short duration.
- Parameters:
- params, optional
PenaltyManager parameters. If not provided, a default will be used.
Methods
Get a cost evaluator for the boosted current penalty values.
Get a cost evaluator for the current penalty values.
register_load_feasible
(is_load_feasible)Registers another capacity feasibility result.
register_time_feasible
(is_time_feasible)Registers another time feasibility result.
- get_booster_cost_evaluator()
Get a cost evaluator for the boosted current penalty values.
- Returns:
CostEvaluator
A CostEvaluator instance that uses the booster penalty values.
- get_cost_evaluator() CostEvaluator
Get a cost evaluator for the current penalty values.
- Returns:
CostEvaluator
A CostEvaluator instance that uses the current penalty values.
- class PopulationParams(min_pop_size: int = 25, generation_size: int = 40, nb_elite: int = 4, nb_close: int = 5, lb_diversity: float = 0.1, ub_diversity: float = 0.5)
- generation_size
- lb_diversity
- property max_pop_size
- min_pop_size
- nb_close
- nb_elite
- ub_diversity
- class Population(diversity_op: ~typing.Callable[[~pyvrp._pyvrp.Solution, ~pyvrp._pyvrp.Solution], float], params: ~pyvrp._pyvrp.PopulationParams = <pyvrp._pyvrp.PopulationParams object>)
Creates a Population instance.
- Parameters:
- diversity_op
Operator to use to determine pairwise diversity between solutions. Have a look at
pyvrp.diversity
for available operators.- params, optional
Population parameters. If not provided, a default will be used.
Methods
add
(solution, cost_evaluator)Adds the given solution to the population.
clear
()Clears the population by removing all solutions currently in the population.
get_tournament
(rng, cost_evaluator[, k])Selects a solution from this population by k-ary tournament, based on the (internal) fitness values of the selected solutions.
Returns the number of feasible solutions in the population.
Returns the number of infeasible solutions in the population.
select
(rng, cost_evaluator[, k])Selects two (if possible non-identical) parents by tournament, subject to a diversity restriction.
- __iter__() Generator[Solution, None, None]
Iterates over the solutions contained in this population.
- Yields:
- iterable
An iterable object of solutions.
- add(solution: Solution, cost_evaluator: CostEvaluator)
Adds the given solution to the population. Survivor selection is automatically triggered when the population reaches its maximum size.
- Parameters:
- solution
Solution to add to the population.
- cost_evaluator
CostEvaluator to use to compute the cost.
- clear()
Clears the population by removing all solutions currently in the population.
- get_tournament(rng: XorShift128, cost_evaluator: CostEvaluator, k: int = 2) Solution
Selects a solution from this population by k-ary tournament, based on the (internal) fitness values of the selected solutions.
- Parameters:
- rng
Random number generator.
- cost_evaluator
Cost evaluator to use when computing the fitness.
- k
The number of solutions to draw for the tournament. Defaults to two, which results in a binary tournament.
- Returns:
Solution
The selected solution.
- num_feasible() int
Returns the number of feasible solutions in the population.
- Returns:
int
Number of feasible solutions.
- num_infeasible() int
Returns the number of infeasible solutions in the population.
- Returns:
int
Number of infeasible solutions.
- select(rng: XorShift128, cost_evaluator: CostEvaluator, k: int = 2) Tuple[Solution, Solution]
Selects two (if possible non-identical) parents by tournament, subject to a diversity restriction.
- Parameters:
- rng
Random number generator.
- cost_evaluator
Cost evaluator to use when computing the fitness.
- k
The number of solutions to draw for the tournament. Defaults to two, which results in a binary tournament.
- Returns:
tuple
A solution pair (parents).
- read(where: str | ~pathlib.Path, instance_format: str = 'vrplib', round_func: str | ~typing.Callable[[~numpy.ndarray], ~numpy.ndarray] = <function no_rounding>) ProblemData
Reads the VRPLIB file at the given location, and returns a ProblemData instance.
- Parameters:
- where
File location to read. Assumes the data on the given location is in VRPLIB format.
- instance_format, optional
File format of the instance to read, one of
'vrplib'
(default) or'solomon'
.- round_func, optional
Optional rounding function. Will be applied to round data if the data is not already integer. This can either be a function or a string:
'round'
rounds the values to the nearest integer;'trunc'
truncates the values to be integral;'trunc1'
or'dimacs'
scale and truncate to the nearest decimal;'none'
does no rounding. This is the default.
- Returns:
ProblemData
Data instance constructed from the read data.
- read_solution(where: str | Path) List[List[int]]
Reads a solution in VRPLIB format from file at the given location, and returns the routes contained in it.
- Parameters:
- where
File location to read. Assumes the solution in the file on the given location is in VRPLIB solution format.
- Returns:
list
List of routes, where each route is a list of client numbers.
- class Result(best: Solution, stats: Statistics, num_iterations: int, runtime: float)
Stores the outcomes of a single run. An instance of this class is returned once the GeneticAlgorithm completes.
- Parameters:
- best
The best observed solution.
- stats
A Statistics object containing runtime statistics. These are only collected and available if statistics were collected for the given run.
- num_iterations
Number of iterations performed by the genetic algorithm.
- runtime
Total runtime of the main genetic algorithm loop.
- Raises:
ValueError
When the number of iterations or runtime are negative.
Methods
cost
()Returns the cost (objective) value of the best solution.
Returns whether detailed statistics were collected.
Returns whether the best solution is feasible.
- cost() float
Returns the cost (objective) value of the best solution. Returns inf if the best solution is infeasible.
- Returns:
float
Objective value.
- show_versions()
This function prints version information that is useful when filing bug reports.
Examples
Calling this function should print information like the following (dependency versions in your local installation will likely differ):
>>> import pyvrp >>> pyvrp.show_versions() INSTALLED VERSIONS ------------------ pyvrp: 1.0.0 numpy: 1.24.2 matplotlib: 3.7.0 vrplib: 1.0.1 tqdm: 4.64.1 tomli: 2.0.1 Python: 3.9.13
- class Statistics(runtimes: ~typing.List[float] = <factory>, num_iterations: int = 0, feas_stats: ~typing.List[~pyvrp.Statistics._Datum] = <factory>, infeas_stats: ~typing.List[~pyvrp.Statistics._Datum] = <factory>)
The Statistics object tracks various (population-level) statistics of genetic algorithm runs. This can be helpful in analysing the algorithm’s performance.
Methods
collect_from
(population, cost_evaluator)Collects statistics from the given population object.
from_csv
(where[, delimiter])Reads a Statistics object from the CSV file at the given filesystem location.
to_csv
(where[, delimiter, quoting])Writes this Statistics object to the given location, as a CSV file.
- collect_from(population: Population, cost_evaluator: CostEvaluator)
Collects statistics from the given population object.
- Parameters:
- population
Population instance to collect statistics from.
- cost_evaluator
CostEvaluator used to compute costs for solutions.
- classmethod from_csv(where: Path | str, delimiter: str = ',', **kwargs)
Reads a Statistics object from the CSV file at the given filesystem location.
- Parameters:
- where
Filesystem location to read from.
- delimiter
Value separator. Default comma.
- kwargs
Additional keyword arguments. These are passed to
csv.DictReader
.
- Returns:
Statistics
Statistics object populated with the data read from the given filesystem location.
- to_csv(where: Path | str, delimiter: str = ',', quoting: int = 0, **kwargs)
Writes this Statistics object to the given location, as a CSV file.
- Parameters:
- where
Filesystem location to write to.
- delimiter
Value separator. Default comma.
- quoting
Quoting strategy. Default only quotes values when necessary.
- kwargs
Additional keyword arguments. These are passed to
csv.DictWriter
.
- class CostEvaluator(capacity_penalty: int = 0, tw_penalty: int = 0)
Creates a CostEvaluator instance.
This class contains time warp and load penalties, and can compute penalties for a given time warp and load.
- Parameters:
- capacity_penalty
The penalty for each unit of excess load over the vehicle capacity.
- tw_penalty
The penalty for each unit of time warp.
- cost(solution: Solution)
Evaluates and returns the cost/objective of the given solution. Hand-waving some details, let \(x_{ij} \in \{ 0, 1 \}\) indicate if edge \((i, j)\) is used in the solution encoded by the given solution, and \(y_i \in \{ 0, 1 \}\) indicate if client \(i\) is visited. The objective is then given by
\[\sum_{(i, j)} d_{ij} x_{ij} + \sum_{i} p_i (1 - y_i),\]where the first part lists the distance costs, and the second part the prizes of the unvisited clients.
- class Route(data: ProblemData, visits: List[int], vehicle_type: int)
A simple class that stores the route plan and some statistics.
- centroid()
Center point of the client locations on this route.
- demand()
Total client demand on this route.
- distance()
Total distance travelled on this route.
- duration()
Total route duration, including waiting time.
- excess_load()
Demand in excess of the vehicle’s capacity.
- has_excess_load()
- has_time_warp()
- is_feasible()
- prizes()
Total prize value collected on this route.
- release_time()
Release time of visits on this route.
- service_duration()
Total duration of service on the route.
- time_warp()
Any time warp incurred along the route.
- vehicle_type()
Index of the type of vehicle used on this route.
- visits()
Route visits, as a list of clients.
- wait_duration()
Total waiting duration on this route.
- class Solution(data: ProblemData, routes: List[Route] | List[List[int]])
Encodes VRP solutions.
- Parameters:
- data
Data instance.
- routes
Route list to use. Can be a list of
Route
objects, or a lists of client visits. In case of the latter, all routes are assigned vehicles of the first type. That need not be a feasible assignment!
- Raises:
RuntimeError
When the number of routes in the
routes
argument exceedsnum_vehicles
, when an empty route has been passed as part ofroutes
, or when too many vehicles of a particular type have been used.
- excess_load()
Returns the total excess load over all routes.
- Returns:
int
Total excess load over all routes.
- get_neighbours()
Returns a list of neighbours for each client, by index. Also includes the depot at index 0, which only neighbours itself.
- Returns:
list
A list of
(pred, succ)
tuples that encode for each client their predecessor and successors in this solutions’s routes.
- get_routes()
The solution’s routing decisions.
Note
The length of this list is equal to the number of non-empty routes, which is at most equal to
num_vehicles
.
- has_excess_load()
Returns whether this solution violates capacity constraints.
- Returns:
- bool
True if the solution is not capacity feasible, False otherwise.
- has_time_warp()
Returns whether this solution violates time window constraints.
- Returns:
- bool
True if the solution is not time window feasible, False otherwise.
- is_feasible()
Whether this solution is feasible. This is a shorthand for checking that
has_excess_load()
andhas_time_warp()
both return false.- Returns:
- bool
Whether the solution of this solution is feasible with respect to capacity and time window constraints.
- classmethod make_random(data: ProblemData, rng: XorShift128)
Creates a randomly generated solution.
- Parameters:
- data
Data instance.
- rng
Random number generator to use.
- Returns:
Solution
The randomly generated solution.
- prizes()
Returns the total collected prize value over all routes.
- Returns:
int
Value of collected prizes.
- class Client(x: int, y: int, demand: int = 0, service_duration: int = 0, tw_early: int = 0, tw_late: int = 0, release_time: int = 0, prize: int = 0, required: bool = True)
Simple data object storing all client data as (read-only) properties.
- Parameters:
- x
Horizontal coordinate of this client, that is, the ‘x’ part of the client’s (x, y) location tuple.
- y
Vertical coordinate of this client, that is, the ‘y’ part of the client’s (x, y) location tuple.
- demand
The amount this client’s demanding. Default 0.
- service_duration
This client’s service duration, that is, the amount of time we need to visit the client for. Service should start (but not necessarily end) within the [
tw_early
,tw_late
] interval. Default 0.- tw_early
Earliest time at which we can visit this client. Default 0.
- tw_late
Latest time at which we can visit this client. Default 0.
- release_time
Earliest time at which this client is released, that is, the earliest time at which a vehicle may leave the depot to visit this client. Default 0.
- prize
Prize collected by visiting this client. Default 0.
- required
Whether this client must be part of a feasible solution. Default True.
- demand
- prize
- release_time
- required
- service_duration
- tw_early
- tw_late
- x
- y
- class VehicleType(capacity: int, num_available: int)
Simple data object storing all vehicle type data as properties.
- Attributes:
- capacity
Capacity (maximum total demand) of this vehicle type.
- num_available
Number of vehicles of this type that are available.
- capacity
- num_available
- class ProblemData(clients: List[Client], vehicle_types: List[VehicleType], distance_matrix: List[List[int]], duration_matrix: List[List[int]])
Creates a problem data instance. This instance contains all information needed to solve the vehicle routing problem.
- Parameters:
- clients
List of clients. The first client (at index 0) is assumed to be the depot. The time window for the depot is assumed to describe the overall time horizon. The depot should have 0 demand and 0 service duration.
- vehicle_types
List of vehicle types in the problem instance.
- duration_matrix
A matrix that gives the travel times between clients (and the depot at index 0).
- centroid()
Center point of all client locations (excluding the depot).
- Returns:
tuple
Centroid of all client locations.
- client(client: int)
Returns client data for the given client.
- Parameters:
- client
Client number whose information to retrieve.
- Returns:
Client
A simple data object containing the requested client’s information.
- depot()
Returns ‘client’ information for the depot, which is stored internally as the client with number 0.
- Returns:
Client
A simple data object containing the depot’s information.
- dist(first: int, second: int)
Returns the travel distance between the first and second argument, according to this instance’s travel distance matrix.
- Parameters:
- first
Client or depot number.
- second
Client or depot number.
- Returns:
int
Travel distance between the given clients.
- duration(first: int, second: int)
Returns the travel duration between the first and second argument, according to this instance’s travel duration matrix.
- Parameters:
- first
Client or depot number.
- second
Client or depot number.
- Returns:
int
Travel duration between the given clients.
- property num_clients
Number of clients in this problem instance.
- Returns:
int
Number of clients in the instance.
- property num_vehicle_types
Number of vehicle types in this problem instance.
- Returns:
int
Number of vehicle types in this problem instance.
- property num_vehicles
Number of vehicles in this problem instance.
- Returns:
int
Number of vehicles in this problem instance.
- vehicle_type(vehicle_type: int)
Returns vehicle type data for the given vehicle type.
- Parameters:
- vehicle_type
Vehicle type number whose information to retrieve.
- Returns:
VehicleType
A simple data object containing the vehicle type information.