Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds maximum travel duration constraint #51

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions factory/constraint_max_travel_duration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// © 2019-present nextmv.io inc

package factory

import (
"time"

"github.com/nextmv-io/nextroute"
"github.com/nextmv-io/nextroute/schema"
)

// addMaximumTravelDurationConstraint
// TODO
func addMaximumTravelDurationConstraint(
input schema.Input,
model nextroute.Model,
_ Options,
) (nextroute.Model, error) {
maximumWaitPerVehicle := nextroute.NewVehicleTypeDurationExpression("vehicle-travel-max", model.MaxDuration())
maximumTravelPresent := addMaximumTravelDurationVehicles(input, model, maximumWaitPerVehicle)

if maximumTravelPresent {
cnstr, err := nextroute.NewMaximumDurationConstraint(maximumWaitPerVehicle)
if err != nil {
return nil, err
}
err = model.AddConstraint(cnstr)
if err != nil {
return nil, err
}
}

return model, nil
}

func addMaximumTravelDurationVehicles(
input schema.Input,
model nextroute.Model,
vehicleLimit nextroute.VehicleTypeDurationExpression,
) bool {
present := false
modelVehicles := model.Vehicles()
for v, inputVehicle := range input.Vehicles {
if inputVehicle.MaxTravelDuration == nil {
continue
}
present = true

vehicleLimit.SetDuration(modelVehicles[v].VehicleType(), time.Duration(*inputVehicle.MaxTravelDuration)*time.Second)
}
return present
}
4 changes: 4 additions & 0 deletions factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ func appendConstraintModifiers(
modifiers = append(modifiers, addMaximumDurationConstraint)
}

if !options.Constraints.Disable.MaximumTravelDuration {
modifiers = append(modifiers, addMaximumTravelDurationConstraint)
}

if !options.Constraints.Disable.Precedence {
modifiers = append(modifiers, addPrecedenceInformation)
}
Expand Down
29 changes: 15 additions & 14 deletions factory/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ package factory
type Options struct {
Constraints struct {
Disable struct {
Attributes bool `json:"attributes" usage:"ignore the compatibility attributes constraint"`
Capacity bool `json:"capacity" usage:"ignore the capacity constraint for all resources"`
Capacities []string `json:"capacities" usage:"ignore the capacity constraint for the given resource names"`
DistanceLimit bool `json:"distance_limit" usage:"ignore the distance limit constraint"`
Groups bool `json:"groups" usage:"ignore the groups constraint"`
MaximumDuration bool `json:"maximum_duration" usage:"ignore the maximum duration constraint"`
MaximumStops bool `json:"maximum_stops" usage:"ignore the maximum stops constraint"`
MaximumWaitStop bool `json:"maximum_wait_stop" usage:"ignore the maximum stop wait constraint"`
MaximumWaitVehicle bool `json:"maximum_wait_vehicle" usage:"ignore the maximum vehicle wait constraint"`
MixingItems bool `json:"mixing_items" usage:"ignore the do not mix items constraint"`
Precedence bool `json:"precedence" usage:"ignore the precedence (pickups & deliveries) constraint"`
VehicleStartTime bool `json:"vehicle_start_time" usage:"ignore the vehicle start time constraint"`
VehicleEndTime bool `json:"vehicle_end_time" usage:"ignore the vehicle end time constraint"`
StartTimeWindows bool `json:"start_time_windows" usage:"ignore the start time windows constraint"`
Attributes bool `json:"attributes" usage:"ignore the compatibility attributes constraint"`
Capacity bool `json:"capacity" usage:"ignore the capacity constraint for all resources"`
Capacities []string `json:"capacities" usage:"ignore the capacity constraint for the given resource names"`
DistanceLimit bool `json:"distance_limit" usage:"ignore the distance limit constraint"`
Groups bool `json:"groups" usage:"ignore the groups constraint"`
MaximumDuration bool `json:"maximum_duration" usage:"ignore the maximum duration constraint"`
MaximumTravelDuration bool `json:"maximum_travel_duration" usage:"ignore the maximum travel duration constraint"`
MaximumStops bool `json:"maximum_stops" usage:"ignore the maximum stops constraint"`
MaximumWaitStop bool `json:"maximum_wait_stop" usage:"ignore the maximum stop wait constraint"`
MaximumWaitVehicle bool `json:"maximum_wait_vehicle" usage:"ignore the maximum vehicle wait constraint"`
MixingItems bool `json:"mixing_items" usage:"ignore the do not mix items constraint"`
Precedence bool `json:"precedence" usage:"ignore the precedence (pickups & deliveries) constraint"`
VehicleStartTime bool `json:"vehicle_start_time" usage:"ignore the vehicle start time constraint"`
VehicleEndTime bool `json:"vehicle_end_time" usage:"ignore the vehicle end time constraint"`
StartTimeWindows bool `json:"start_time_windows" usage:"ignore the start time windows constraint"`
} `json:"disable"`
Enable struct {
Cluster bool `json:"cluster" usage:"enable the cluster constraint"`
Expand Down
17 changes: 14 additions & 3 deletions model_constraint_maximum_duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,18 @@ func (l *maximumDurationConstraintImpl) EstimateIsViolated(
) (isViolated bool, stopPositionsHint StopPositionsHint) {
moveImpl := move.(*solutionMoveStopsImpl)
vehicle := moveImpl.vehicle()

if vehicle.IsEmpty() {
return false, constNoPositionsHint
}

vehicleType := vehicle.ModelVehicle().VehicleType()

dependentOnTime := vehicleType.TravelDurationExpression().IsDependentOnTime()

maximumValue := l.maximum.Value(vehicleType, nil, nil)

startValue := vehicle.first().StartValue()
startValue := vehicle.first().next().StartValue()
previous, _ := moveImpl.previous()
endValue := previous.EndValue()

Expand Down Expand Up @@ -97,8 +102,14 @@ func (l *maximumDurationConstraintImpl) EstimateIsViolated(
}

func (l *maximumDurationConstraintImpl) DoesVehicleHaveViolations(vehicle SolutionVehicle) bool {
return vehicle.DurationValue() >
l.maximum.Value(vehicle.ModelVehicle().VehicleType(), nil, nil)
if vehicle.IsEmpty() {
return false
}
// TODO: improve
endValue := vehicle.Last().EndValue() - vehicle.First().Next().StartValue()
max := l.maximum.Value(vehicle.ModelVehicle().VehicleType(), nil, nil)
return endValue > max

}

func (l *maximumDurationConstraintImpl) IsTemporal() bool {
Expand Down
6 changes: 5 additions & 1 deletion schema/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,12 @@ type Vehicle struct {
MaxStops *int `json:"max_stops,omitempty"`
// Speed of the vehicle in meters per second.
Speed *float64 `json:"speed,omitempty" minimumExclusive:"0"`
// MaxDuration maximum duration in seconds that the vehicle can travel.
// MaxDuration maximum duration in seconds that the vehicle can travel after
// its start time.
MaxDuration *int `json:"max_duration,omitempty" minimum:"0"`
// MaxTravelDuration maximum duration in seconds that the vehicle can
// travel between first and last stop.
MaxTravelDuration *int `json:"max_travel_duration,omitempty" minimum:"0"`
// MaxWait maximum aggregated waiting time that the vehicle can wait across route stops.
MaxWait *int `json:"max_wait,omitempty" minimum:"0"`
// ActivationPenalty penalty of using the vehicle.
Expand Down
25 changes: 25 additions & 0 deletions tests/golden/testdata/max_duration_early_start.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"defaults": {
"vehicles": {
"speed": 5,
"start_time": "2023-01-01T12:00:00Z"
},
"stops": {
"unplanned_penalty": 2000000000,
"duration": 300
}
},
"stops": [
{
"id": "Fushimi Inari Taisha",
"location": { "lon": 135.772695, "lat": 34.967146 },
"start_time_window": ["2023-01-01T18:00:00Z", "2023-01-01T18:05:00Z"]
}
],
"vehicles": [
{
"id": "v1",
"max_travel_duration": 3600
}
]
}
Loading