You are viewing Nextmv legacy docs. ⚡️ Go to latest docs ⚡️

Router

Windows

You will learn how to use the Windows option with a practical example.

In vehicle routing problems (VRPs) stops often have to be serviced within specific time windows. E.g., in a bike sharing problem the stations have to be filled up with bikes while they are empty or bikes have to be picked up while they are full. This is known as the vehicle routing problem with time windows (VRPTW).

The router engine provides the Windows option to configure time windows for stops.

The Windows option must be used together with the Shifts option.

The Windows option is often used together with the Services option.

Example

The router example is used as a base, where routes are created to visit seven landmarks in Kyoto using two vehicles. Here, we define time windows and service times for certain stops and define shifts for both vehicles.

windows-input

Save the following information in an input.json file (see input and output for more information on working with input files).

{
  "stops": [
    {
      "id": "Fushimi Inari Taisha",
      "position": { "lon": 135.772695, "lat": 34.967146 }
    },
    {
      "id": "Kiyomizu-dera",
      "position": { "lon": 135.78506, "lat": 34.994857 }
    },
    {
      "id": "Nijō Castle",
      "position": { "lon": 135.748134, "lat": 35.014239 }
    },
    {
      "id": "Kyoto Imperial Palace",
      "position": { "lon": 135.762057, "lat": 35.025431 }
    },
    {
      "id": "Gionmachi",
      "position": { "lon": 135.775682, "lat": 35.002457 }
    },
    {
      "id": "Kinkaku-ji",
      "position": { "lon": 135.728898, "lat": 35.039705 }
    },
    {
      "id": "Arashiyama Bamboo Forest",
      "position": { "lon": 135.672009, "lat": 35.017209 }
    }
  ],
  "vehicles": ["v1", "v2"],
  "hard_windows": [
    {
      "time_window": {
        "start": "2020-10-17T10:00:00-06:00",
        "end": "2020-10-17T12:00:00-06:00"
      },
      "max_wait": -1
    },
    {
      "time_window": {
        "start": "2020-10-17T10:00:00-06:00",
        "end": "2020-10-17T12:00:00-06:00"
      },
      "max_wait": -1
    },
    {},
    {},
    {},
    {
      "time_window": {
        "start": "2020-10-17T12:00:00-06:00",
        "end": "2020-10-17T14:00:00-06:00"
      },
      "max_wait": -1
    },
    {
      "time_window": {
        "start": "2020-10-17T12:00:00-06:00",
        "end": "2020-10-17T14:00:00-06:00"
      },
      "max_wait": -1
    }
  ],
  "shifts": [
    {
      "start": "2020-10-17T10:00:00-06:00",
      "end": "2020-10-17T12:00:00-06:00"
    },
    {
      "start": "2020-10-17T12:00:00-06:00",
      "end": "2020-10-17T14:00:00-06:00"
    }
  ],
  "service_times": [
    {
      "id": "Fushimi Inari Taisha",
      "duration": 900
    },
    {
      "id": "Kiyomizu-dera",
      "duration": 900
    },
    {
      "id": "Kyoto Imperial Palace",
      "duration": 900
    },
    {
      "id": "Kinkaku-ji",
      "duration": 900
    }
  ]
}
Copy

Code

The following program uses the CLI Runner to obtain a solution and requires access to the Nextmv code repository on GitHub. To request access, please contact support@nextmv.io.

To proceed with running the example, create a main.go file and use the code snippet below.

package main

import (
    "github.com/nextmv-io/code/engines/route"
    "github.com/nextmv-io/code/hop/run/cli"
    "github.com/nextmv-io/code/hop/solve"
)

// Struct to read from JSON in.
type input struct {
    Stops       []route.Stop       `json:"stops,omitempty"`
    Vehicles    []string           `json:"vehicles,omitempty"`
    HardWindows []route.Window     `json:"hard_windows,omitempty"`
    Shifts      []route.TimeWindow `json:"shifts,omitempty"`
    Services    []route.Service    `json:"service_times,omitempty"`
}

// Use the CLI runner to solve a Vehicle Routing Problem.
func main() {
    f := func(i input, opt solve.Options) (solve.Solver, error) {
        router, err := route.NewRouter(
            i.Stops,
            i.Vehicles,
            route.Shifts(i.Shifts),
            route.Windows(i.HardWindows),
            // Using service times is optional
            route.Services(i.Services),
        )
        if err != nil {
            return nil, err
        }

        return router.Solver(opt)
    }

    cli.Run(f)
}
Copy

To execute the example, specify the path to the input.json file using command-line flags and use jq to extract the solution state (see runners for more information on building and running programs).

go run main.go -hop.runner.input.path input.json | jq .state
Copy

Solution

The solution should look similar to this one:

{
  "unassigned": [],
  "vehicles": [
    {
      "id": "v1",
      "route": [
        {
          "id": "Fushimi Inari Taisha",
          "position": {
            "lon": 135.772695,
            "lat": 34.967146
          },
          "estimated_arrival": "2020-10-17T10:00:00-06:00",
          "estimated_departure": "2020-10-17T10:15:00-06:00"
        },
        {
          "id": "Kiyomizu-dera",
          "position": {
            "lon": 135.78506,
            "lat": 34.994857
          },
          "estimated_arrival": "2020-10-17T10:20:28-06:00",
          "estimated_departure": "2020-10-17T10:35:28-06:00"
        },
        {
          "id": "Gionmachi",
          "position": {
            "lon": 135.775682,
            "lat": 35.002457
          },
          "estimated_arrival": "2020-10-17T10:37:28-06:00",
          "estimated_departure": "2020-10-17T10:37:28-06:00"
        },
        {
          "id": "Nijō Castle",
          "position": {
            "lon": 135.748134,
            "lat": 35.014239
          },
          "estimated_arrival": "2020-10-17T10:42:11-06:00",
          "estimated_departure": "2020-10-17T10:42:11-06:00"
        },
        {
          "id": "Kyoto Imperial Palace",
          "position": {
            "lon": 135.762057,
            "lat": 35.025431
          },
          "estimated_arrival": "2020-10-17T10:45:09-06:00",
          "estimated_departure": "2020-10-17T11:00:09-06:00"
        }
      ],
      "route_duration": 3609
    },
    {
      "id": "v2",
      "route": [
        {
          "id": "Kinkaku-ji",
          "position": {
            "lon": 135.728898,
            "lat": 35.039705
          },
          "estimated_arrival": "2020-10-17T12:00:00-06:00",
          "estimated_departure": "2020-10-17T12:15:00-06:00"
        },
        {
          "id": "Arashiyama Bamboo Forest",
          "position": {
            "lon": 135.672009,
            "lat": 35.017209
          },
          "estimated_arrival": "2020-10-17T12:24:35-06:00",
          "estimated_departure": "2020-10-17T12:24:35-06:00"
        }
      ],
      "route_duration": 1475
    }
  ]
}
Copy

You can see that given the shift availability of the vehicles, stops Fushimi Inari Taisha and Kiyomizu-dera are assigned to vehicle v1 as they must be serviced before 12 pm. On the other hand, stops Arashiyama Bamboo Forest and Kinkaku-ji are assigned to vehicle v2, given that they must be visited after mid-day.

windows-output

Page last updated

Go to on-page nav menu