High-level builder for constructing VRP problems.
The Model provides a fluent API for defining depots, vehicle types, clients, and routing constraints. A model must have at least one depot and one vehicle type before it can be solved.
Basic Example
model =
ExVrp.Model.new()
|> ExVrp.Model.add_depot(x: 0, y: 0)
|> ExVrp.Model.add_vehicle_type(num_available: 2, capacity: [100])
|> ExVrp.Model.add_client(x: 1, y: 1, delivery: [10])
|> ExVrp.Model.add_client(x: 2, y: 2, delivery: [20])
|> ExVrp.Model.add_client(x: 3, y: 1, delivery: [15])
{:ok, result} = ExVrp.solve(model)Custom Distance/Duration Matrices
By default, Euclidean distances are computed from coordinates. You can provide custom matrices instead (one per vehicle profile):
# Matrix rows/columns: [depot, client1, client2, ...]
distances = [
[0, 100, 200],
[100, 0, 150],
[200, 150, 0]
]
model =
ExVrp.Model.new()
|> ExVrp.Model.add_depot(x: 0, y: 0)
|> ExVrp.Model.add_client(x: 1, y: 0, delivery: [10])
|> ExVrp.Model.add_client(x: 2, y: 0, delivery: [20])
|> ExVrp.Model.add_vehicle_type(num_available: 2, capacity: [100])
|> ExVrp.Model.set_distance_matrices([distances])
|> ExVrp.Model.set_duration_matrices([distances])Multi-Dimensional Capacity
Vehicles and clients can have multiple capacity dimensions (e.g. weight and volume):
model
|> ExVrp.Model.add_vehicle_type(num_available: 3, capacity: [1000, 50])
|> ExVrp.Model.add_client(x: 1, y: 1, delivery: [200, 10])Client Groups
Client groups allow mutually exclusive alternatives — only one client from the group will be visited:
{model, group} = ExVrp.Model.add_client_group(model, required: false)
model =
model
|> ExVrp.Model.add_client(x: 1, y: 1, group: group, required: false, prize: 100)
|> ExVrp.Model.add_client(x: 2, y: 2, group: group, required: false, prize: 150)Same-Vehicle Groups
Force specific clients onto the same route:
[c1, c2] = model.clients
model = ExVrp.Model.add_same_vehicle_group(model, [c1, c2])Validation
Models are validated automatically before solving. You can also validate explicitly:
case ExVrp.Model.validate(model) do
:ok -> :ready
{:error, reasons} -> IO.inspect(reasons)
end
Summary
Functions
Adds a client to the model.
Adds a new client group to the model.
Adds a depot to the model.
Adds a same-vehicle constraint group to the model.
Adds a vehicle type to the model.
Creates a new empty model.
Returns the number of clients in the model.
Returns the number of depots in the model.
Returns the total number of locations (depots + clients) in the model.
Returns the number of vehicle types in the model.
Returns the total number of vehicles in the model.
Sets custom distance matrices.
Sets custom duration matrices.
Converts the model to ProblemData for the solver.
Validates the model and returns any errors.
Types
@type t() :: %ExVrp.Model{ client_groups: [ExVrp.ClientGroup.t()], clients: [ExVrp.Client.t()], depots: [ExVrp.Depot.t()], distance_matrices: [[[non_neg_integer()]]], duration_matrices: [[[non_neg_integer()]]], same_vehicle_groups: [ExVrp.SameVehicleGroup.t()], vehicle_types: [ExVrp.VehicleType.t()] }
Functions
Adds a client to the model.
See ExVrp.Client.new/1 for available options.
Options
:group- Group index fromadd_client_group/2(optional)- See
ExVrp.Client.new/1for other options
Example
model
|> ExVrp.Model.add_client(x: 1, y: 2, delivery: [10])
# With group assignment
{model, group} = Model.add_client_group(model, required: false)
model = Model.add_client(model, x: 1, y: 1, group: group)Raises
ArgumentErrorif group index is invalidArgumentErrorif required client is added to mutually exclusive group
@spec add_client_group( t(), keyword() ) :: {t(), non_neg_integer()}
Adds a new client group to the model.
Returns {model, group_index} where group_index can be passed to
add_client/2 to dynamically add clients to the group.
Options
:required- Whether at least one client must be visited (default:true):mutually_exclusive- Whether only one client can be visited (default:not required):name- Group name for identification (default:"")
Example
{model, group} = Model.add_client_group(model, required: false)
model = Model.add_client(model, x: 1, y: 1, group: group)
model = Model.add_client(model, x: 2, y: 2, group: group)
Adds a depot to the model.
See ExVrp.Depot.new/1 for available options.
Note: When adding a depot after clients have been added, all client group indices are recalculated to account for the new depot shifting client indices.
Example
model
|> ExVrp.Model.add_depot(x: 0, y: 0)
@spec add_same_vehicle_group(t(), [ExVrp.Client.t()], keyword()) :: t()
Adds a same-vehicle constraint group to the model.
All clients in this group that are visited must be served by the same vehicle. It is allowed to visit only a subset of the group (or none at all), but any visited clients must share a route.
Parameters
model- The model to add the group toclients- List of Client structs that must be served by the same vehicle
Options
:name- Free-form name for the group (default:"")
Returns
The updated model with the new same-vehicle group added.
Example
model =
Model.new()
|> Model.add_depot(x: 0, y: 0)
|> Model.add_client(x: 1, y: 1)
|> Model.add_client(x: 2, y: 2)
|> Model.add_vehicle_type(num_available: 2)
[c1, c2] = model.clients
model = Model.add_same_vehicle_group(model, [c1, c2], name: "group1")Raises
ArgumentErrorif any client is not in the model
Adds a vehicle type to the model.
See ExVrp.VehicleType.new/1 for available options.
Example
model
|> ExVrp.Model.add_vehicle_type(num_available: 3, capacity: [100])
@spec new() :: t()
Creates a new empty model.
@spec num_clients(t()) :: non_neg_integer()
Returns the number of clients in the model.
@spec num_depots(t()) :: non_neg_integer()
Returns the number of depots in the model.
@spec num_locations(t()) :: non_neg_integer()
Returns the total number of locations (depots + clients) in the model.
@spec num_vehicle_types(t()) :: non_neg_integer()
Returns the number of vehicle types in the model.
@spec num_vehicles(t()) :: non_neg_integer()
Returns the total number of vehicles in the model.
@spec set_distance_matrices(t(), [[[non_neg_integer()]]]) :: t()
Sets custom distance matrices.
If not provided, Euclidean distances are computed from coordinates.
Example
model
|> ExVrp.Model.set_distance_matrices([matrix1, matrix2])
@spec set_duration_matrices(t(), [[[non_neg_integer()]]]) :: t()
Sets custom duration matrices.
If not provided, distances are used as durations.
Example
model
|> ExVrp.Model.set_duration_matrices([matrix1, matrix2])
Converts the model to ProblemData for the solver.
This is called internally by ExVrp.solve/2.
Validates the model and returns any errors.
Returns :ok if valid, {:error, reasons} otherwise.