From 2b36515b43e6ce11d26e3a8ac8e97f30d66a3ccf Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Fri, 8 Mar 2019 17:21:54 +0100 Subject: [PATCH] Add VRP Resources samples --- makefiles/Makefile.cpp.mk | 1 + makefiles/Makefile.dotnet.mk | 1 + makefiles/Makefile.java.mk | 1 + makefiles/Makefile.python.mk | 1 + ortools/constraint_solver/doc/generate_svg.sh | 8 +- ortools/constraint_solver/doc/vrp.md | 13 + .../constraint_solver/doc/vrp_resources.svg | 96 ++++++++ .../doc/vrp_resources_solution.svg | 150 +++++++++++ .../constraint_solver/samples/VrpResources.cs | 232 ++++++++++++++++++ .../samples/VrpResources.csproj | 22 ++ .../samples/VrpResources.java | 230 +++++++++++++++++ .../samples/vrp_resources.cc | 231 +++++++++++++++++ .../samples/vrp_resources.py | 227 +++++++++++++++++ 13 files changed, 1209 insertions(+), 4 deletions(-) create mode 100644 ortools/constraint_solver/doc/vrp_resources.svg create mode 100644 ortools/constraint_solver/doc/vrp_resources_solution.svg create mode 100644 ortools/constraint_solver/samples/VrpResources.cs create mode 100644 ortools/constraint_solver/samples/VrpResources.csproj create mode 100644 ortools/constraint_solver/samples/VrpResources.java create mode 100644 ortools/constraint_solver/samples/vrp_resources.cc create mode 100644 ortools/constraint_solver/samples/vrp_resources.py diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index 9f1d4a01a4..2e580a8c43 100755 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -381,6 +381,7 @@ test_cc_constraint_solver_samples: \ rcc_vrp_pickup_delivery \ rcc_vrp_pickup_delivery_fifo \ rcc_vrp_pickup_delivery_lifo \ + rcc_vrp_resources \ rcc_vrp_starts_ends \ rcc_vrp_time_windows \ diff --git a/makefiles/Makefile.dotnet.mk b/makefiles/Makefile.dotnet.mk index 155fd5e392..63a5a496de 100644 --- a/makefiles/Makefile.dotnet.mk +++ b/makefiles/Makefile.dotnet.mk @@ -453,6 +453,7 @@ test_dotnet_constraint_solver_samples: $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpPickupDelivery.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpPickupDeliveryFifo.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpPickupDeliveryLifo.cs + $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpResources.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpStartsEnds.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpTimeWindows.cs diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index dfdce49081..9befd7c5a9 100755 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -386,6 +386,7 @@ test_java_constraint_solver_samples: \ rjava_VrpPickupDelivery \ rjava_VrpPickupDeliveryFifo \ rjava_VrpPickupDeliveryLifo \ + rjava_VrpResources \ rjava_VrpStartsEnds \ rjava_VrpTimeWindows \ diff --git a/makefiles/Makefile.python.mk b/makefiles/Makefile.python.mk index 9094c261f3..9008928748 100755 --- a/makefiles/Makefile.python.mk +++ b/makefiles/Makefile.python.mk @@ -512,6 +512,7 @@ test_python_constraint_solver_samples: \ rpy_vrp_pickup_delivery \ rpy_vrp_pickup_delivery_fifo \ rpy_vrp_pickup_delivery_lifo \ + rpy_vrp_resources \ rpy_vrp_starts_ends \ rpy_vrp_time_windows \ \ diff --git a/ortools/constraint_solver/doc/generate_svg.sh b/ortools/constraint_solver/doc/generate_svg.sh index 2f603ae0ad..1e04eee776 100755 --- a/ortools/constraint_solver/doc/generate_svg.sh +++ b/ortools/constraint_solver/doc/generate_svg.sh @@ -31,6 +31,10 @@ set -x ./routing_svg.py --time-windows > vrp_time_windows.svg ./routing_svg.py --time-windows --solution > vrp_time_windows_solution.svg +# Ressource Problem +./routing_svg.py --resources > vrp_resources.svg +./routing_svg.py --resources --solution > vrp_resources_solution.svg + # VRP Starts Ends ./routing_svg.py --starts-ends > vrp_starts_ends.svg ./routing_svg.py --starts-ends --solution > vrp_starts_ends_solution.svg @@ -47,7 +51,3 @@ set -x ## Fuel Problem #./routing_svg.py --fuel > vrpf.svg #./routing_svg.py --fuel --solution > vrpf_solution.svg -# -## Ressource Problem -#./routing_svg.py --resource > vrpr.svg -#./routing_svg.py --resource --solution > vrpr_solution.svg diff --git a/ortools/constraint_solver/doc/vrp.md b/ortools/constraint_solver/doc/vrp.md index 9ff1485798..494e17b7b7 100644 --- a/ortools/constraint_solver/doc/vrp.md +++ b/ortools/constraint_solver/doc/vrp.md @@ -82,3 +82,16 @@ Samples: * [VrpTimeWindows.java](../samples/VrpTimeWindows.java) * [VrpTimeWindows.cs](../samples/VrpTimeWindows.cs) +## Resource Constraints +Data Problem: +![problem](vrp_resources.svg) + +Solution: +![solution](vrp_resources_solution.svg) + +Samples: +* [vrp_resources.cc](../samples/vrp_resources.cc) +* [vrp_resources.py](../samples/vrp_resources.py) +* [VrpResources.java](../samples/VrpResources.java) +* [VrpResources.cs](../samples/VrpResources.cs) + diff --git a/ortools/constraint_solver/doc/vrp_resources.svg b/ortools/constraint_solver/doc/vrp_resources.svg new file mode 100644 index 0000000000..8f348a0e7f --- /dev/null +++ b/ortools/constraint_solver/doc/vrp_resources.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1 + +2 + +3 + +4 + +5 + +6 + +7 + +8 + +9 + +10 + +11 + +12 + +13 + +14 + +15 + +16 + + +0 + +[7,12] +[10,15] +[5,14] +[5,13] +[0,5] +[5,10] +[0,10] +[5,10] +[0,5] +[10,16] +[10,15] +[0,5] +[5,10] +[7,12] +[10,15] +[5,15] + diff --git a/ortools/constraint_solver/doc/vrp_resources_solution.svg b/ortools/constraint_solver/doc/vrp_resources_solution.svg new file mode 100644 index 0000000000..31eb60eb05 --- /dev/null +++ b/ortools/constraint_solver/doc/vrp_resources_solution.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +8 + +14 + +16 + + + + + + + +12 + +13 + +15 + +11 + + + + + + + +7 + +1 + +4 + +3 + + + + + + + + +9 + +5 + +6 + +2 + +10 + + + +0 + +[7,12] +[10,15] +[5,14] +[5,13] +[0,5] +[5,10] +[0,10] +[5,10] +[0,5] +[10,16] +[10,15] +[0,5] +[5,10] +[7,12] +[10,15] +[5,15] + + +[5] +[8] +[11] +[13] +[20] + +[0] +[4] +[6] +[11] +[14] +[20] + +[5] +[7] +[11] +[13] +[14] +[25] + +[0] +[2] +[4] +[6] +[10] +[14] +[25] + diff --git a/ortools/constraint_solver/samples/VrpResources.cs b/ortools/constraint_solver/samples/VrpResources.cs new file mode 100644 index 0000000000..b322b634de --- /dev/null +++ b/ortools/constraint_solver/samples/VrpResources.cs @@ -0,0 +1,232 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +using System; +using System.Linq; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Vehicles Routing Problem (VRP) with Resource Constraints. +/// +public class VrpResources { + // [START data_model] + class DataModel { + public long[,] TimeMatrix = { + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + public long[,] TimeWindows = { + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {5, 14}, // 3 + {5, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 10}, // 7 + {5, 10}, // 8 + {0, 5}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 12}, // 14 + {10, 15}, // 15 + {5, 15}, // 16 + }; + public int VehicleNumber = 4; + // [START resources_data] + public int VehicleLoadTime = 5; + public int VehicleUnloadTime = 5; + public int DepotCapacity = 2; + // [END resources_data] + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + // Inspect solution. + long totalTime = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + var timeVar = timeDimension.CumulVar(index); + var slackVar = timeDimension.SlackVar(index); + Console.Write("{0} Time({1},{2}) Slack({3},{4}) -> ", + manager.IndexToNode(index), + solution.Min(timeVar), + solution.Max(timeVar), + solution.Min(slackVar), + solution.Max(slackVar)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + } + var endTimeVar = timeDimension.CumulVar(index); + Console.WriteLine("{0} Time({1},{2})", + manager.IndexToNode(index), + solution.Min(endTimeVar), + solution.Max(endTimeVar)); + Console.WriteLine("Time of the route: {0}min", solution.Min(endTimeVar)); + totalTime += solution.Min(endTimeVar); + } + Console.WriteLine("Total time of all routes: {0}min", totalTime); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.TimeMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.TimeMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START time_constraint] + routing.AddDimension( + transitCallbackIndex, // transit callback + 30, // allow waiting time + 30, // vehicle maximum capacities + false, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + // Add time window constraints for each location except depot + // and 'copy' the slack var in the solution object (aka Assignment) to print it + for (int i = 1; i < data.TimeWindows.GetLength(0); ++i) { + long index = manager.NodeToIndex(i); + timeDimension.CumulVar(index).SetRange( + data.TimeWindows[i, 0], + data.TimeWindows[i, 1]); + routing.AddToAssignment(timeDimension.SlackVar(index)); + } + // Add time window constraints for each vehicle start node + // and 'copy' the slack var in the solution object (aka Assignment) to print + // it + for (int i = 0; i < data.VehicleNumber; ++i) { + long index = routing.Start(i); + timeDimension.CumulVar(index).SetRange( + data.TimeWindows[0, 0], + data.TimeWindows[0, 1]); + routing.AddToAssignment(timeDimension.SlackVar(index)); + } + // [END time_constraint] + + // Add resource constraints at the depot. + // [START depot_load_time] + Solver solver = routing.solver(); + IntervalVar[] intervals = new IntervalVar[ data.VehicleNumber * 2 ]; + for (int i = 0; i < data.VehicleNumber; ++i) { + // Add load duration at start of routes + intervals[2*i] = solver.MakeFixedDurationIntervalVar( + timeDimension.CumulVar(routing.Start(i)), data.VehicleLoadTime, + "depot_interval"); + // Add unload duration at end of routes. + intervals[2*i+1] = solver.MakeFixedDurationIntervalVar( + timeDimension.CumulVar(routing.End(i)), data.VehicleUnloadTime, + "depot_interval"); + } + // [END depot_load_time] + + // [START depot_capacity] + long[] depot_usage = Enumerable.Repeat(1, intervals.Length).ToArray(); + solver.Add(solver.MakeCumulative(intervals, depot_usage, + data.DepotCapacity, "depot")); + // [END depot_capacity] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.VehicleNumber; ++i) { + routing.AddVariableMinimizedByFinalizer( + timeDimension.CumulVar(routing.Start(i))); + routing.AddVariableMinimizedByFinalizer( + timeDimension.CumulVar(routing.End(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/ortools/constraint_solver/samples/VrpResources.csproj b/ortools/constraint_solver/samples/VrpResources.csproj new file mode 100644 index 0000000000..5c978d5514 --- /dev/null +++ b/ortools/constraint_solver/samples/VrpResources.csproj @@ -0,0 +1,22 @@ + + + Exe + 7.2 + netcoreapp2.1 + false + ../../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.VrpResources + true + + + + full + true + true + + + + + + + diff --git a/ortools/constraint_solver/samples/VrpResources.java b/ortools/constraint_solver/samples/VrpResources.java new file mode 100644 index 0000000000..bf42d23cdb --- /dev/null +++ b/ortools/constraint_solver/samples/VrpResources.java @@ -0,0 +1,230 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.IntervalVar; +import com.google.ortools.constraintsolver.RoutingDimension; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.Solver; +import com.google.ortools.constraintsolver.main; +import java.util.Arrays; +import java.util.logging.Logger; +// [END import] + +/** Minimal VRP with Resource Constraints.*/ +public class VrpResources { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpResources.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] timeMatrix = { + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + public final long[][] timeWindows = { + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {5, 14}, // 3 + {5, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 10}, // 7 + {5, 10}, // 8 + {0, 5}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 12}, // 14 + {10, 15}, // 15 + {5, 15}, // 16 + }; + public final int vehicleNumber = 4; + // [START resources_data] + public final int vehicleLoadTime = 5; + public final int vehicleUnloadTime = 5; + public final int depotCapacity = 2; + // [END resources_data] + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + logger.info("Objective : " + solution.objectiveValue()); + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + long totalTime = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + String route = ""; + while (!routing.isEnd(index)) { + IntVar timeVar = timeDimension.cumulVar(index); + IntVar slackVar = timeDimension.slackVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ") Slack(" + solution.min(slackVar) + "," + + solution.max(slackVar) + ") -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + } + IntVar timeVar = timeDimension.cumulVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ")"; + logger.info(route); + logger.info("Time of the route: " + solution.min(timeVar) + "min"); + totalTime += solution.min(timeVar); + } + logger.info("Total time of all routes: " + totalTime + "min"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.timeMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.timeMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Time constraint. + // [START time_constraint] + routing.addDimension(transitCallbackIndex, // transit callback + 30, // allow waiting time + 30, // vehicle maximum capacities + false, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + // Add time window constraints for each location except depot + // and 'copy' the slack var in the solution object (aka Assignment) to print it + for (int i = 1; i < data.timeWindows.length; ++i) { + long index = manager.nodeToIndex(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[i][0], data.timeWindows[i][1]); + routing.addToAssignment(timeDimension.slackVar(index)); + } + // Add time window constraints for each vehicle start node + // and 'copy' the slack var in the solution object (aka Assignment) to print + // it + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[0][0], data.timeWindows[0][1]); + routing.addToAssignment(timeDimension.slackVar(index)); + } + // [END time_constraint] + + // Add resource constraints at the depot. + // [START depot_load_time] + Solver solver = routing.solver(); + IntervalVar[] intervals = new IntervalVar[ data.vehicleNumber * 2 ]; + for (int i = 0; i < data.vehicleNumber; ++i) { + // Add load duration at start of routes + intervals[2*i] = solver.makeFixedDurationIntervalVar( + timeDimension.cumulVar(routing.start(i)), data.vehicleLoadTime, + "depot_interval"); + // Add unload duration at end of routes. + intervals[2*i+1] = solver.makeFixedDurationIntervalVar( + timeDimension.cumulVar(routing.end(i)), data.vehicleUnloadTime, + "depot_interval"); + } + // [END depot_load_time] + + // [START depot_capacity] + long[] depot_usage = new long[ intervals.length ]; + Arrays.fill(depot_usage, 1); + solver.addConstraint(solver.makeCumulative(intervals, depot_usage, + data.depotCapacity, "depot")); + // [END depot_capacity] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.vehicleNumber; ++i) { + routing.addVariableMinimizedByFinalizer( + timeDimension.cumulVar(routing.start(i))); + routing.addVariableMinimizedByFinalizer( + timeDimension.cumulVar(routing.end(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/ortools/constraint_solver/samples/vrp_resources.cc b/ortools/constraint_solver/samples/vrp_resources.cc new file mode 100644 index 0000000000..a0ccafc4aa --- /dev/null +++ b/ortools/constraint_solver/samples/vrp_resources.cc @@ -0,0 +1,231 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +#include +#include +#include "ortools/constraint_solver/routing.h" +#include "ortools/constraint_solver/routing_enums.pb.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> time_matrix{ + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + const std::vector> time_windows{ + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {5, 14}, // 3 + {5, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 10}, // 7 + {5, 10}, // 8 + {0, 5}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 12}, // 14 + {10, 15}, // 15 + {5, 15}, // 16 + }; + const int num_vehicles = 4; + // [START resources_data] + const int vehicle_load_time = 5; + const int vehicle_unload_time = 5; + const int depot_capacity = 2; + // [END resources_data] + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + LOG(INFO) << "Objective: " << solution.ObjectiveValue(); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie("Time"); + int64 total_time{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for vehicle " << vehicle_id << ":"; + std::ostringstream route; + while (routing.IsEnd(index) == false) { + auto time_var = time_dimension.CumulVar(index); + auto slack_var = time_dimension.SlackVar(index); + route << manager.IndexToNode(index).value() << " Time(" + << solution.Min(time_var) << ", " << solution.Max(time_var) + << ") Slack(" << solution.Min(slack_var) << ", " + << solution.Max(slack_var) << ") -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + } + auto time_var = time_dimension.CumulVar(index); + LOG(INFO) << route.str() << manager.IndexToNode(index).value() << " Time(" + << solution.Min(time_var) << ", " << solution.Max(time_var) + << ")"; + LOG(INFO) << "Time of the route: " << solution.Min(time_var) << "min"; + total_time += solution.Min(time_var); + } + LOG(INFO) << "Total time of all routes: " << total_time << "min"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpTimeWindows() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.time_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to time matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.time_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Time constraint. + // [START time_constraint] + std::string time{"Time"}; + routing.AddDimension(transit_callback_index, // transit callback index + int64{30}, // allow waiting time + int64{30}, // maximum time per vehicle + false, // Don't force start cumul to zero + time); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(time); + // Add time window constraints for each location except depot + // and 'copy' the slack var in the solution object (aka Assignment) to print + // it + for (int i = 1; i < data.time_windows.size(); ++i) { + int64 index = manager.NodeToIndex(RoutingIndexManager::NodeIndex(i)); + time_dimension.CumulVar(index)->SetRange(data.time_windows[i].first, + data.time_windows[i].second); + routing.AddToAssignment(time_dimension.SlackVar(index)); + } + // Add time window constraints for each vehicle start node + // and 'copy' the slack var in the solution object (aka Assignment) to print + // it + for (int i = 0; i < data.num_vehicles; ++i) { + int64 index = routing.Start(i); + time_dimension.CumulVar(index)->SetRange(data.time_windows[0].first, + data.time_windows[0].second); + routing.AddToAssignment(time_dimension.SlackVar(index)); + } + // [END time_constraint] + + // Add resource constraints at the depot. + // [START depot_load_time] + Solver* solver = routing.solver(); + std::vector intervals; + for (int i = 0; i < data.num_vehicles; ++i) { + // Add load duration at start of routes + intervals.push_back(solver->MakeFixedDurationIntervalVar( + time_dimension.CumulVar(routing.Start(i)), data.vehicle_load_time, + "depot_interval")); + // Add unload duration at end of routes. + intervals.push_back(solver->MakeFixedDurationIntervalVar( + time_dimension.CumulVar(routing.End(i)), data.vehicle_unload_time, + "depot_interval")); + } + // [END depot_load_time] + + // [START depot_capacity] + std::vector depot_usage(intervals.size(), 1); + solver->AddConstraint(solver->MakeCumulative(intervals, depot_usage, + data.depot_capacity, "depot")); + // [END depot_capacity] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.num_vehicles; ++i) { + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.End(i))); + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.Start(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpTimeWindows(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/ortools/constraint_solver/samples/vrp_resources.py b/ortools/constraint_solver/samples/vrp_resources.py new file mode 100644 index 0000000000..49dc23613b --- /dev/null +++ b/ortools/constraint_solver/samples/vrp_resources.py @@ -0,0 +1,227 @@ +# Copyright 2010-2018 Google LLC +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# [START program] +"""Vehicles Routing Problem (VRP) with Resource Constraints.""" + +# [START import] +from __future__ import print_function +from ortools.constraint_solver import routing_enums_pb2 +from ortools.constraint_solver import pywrapcp + +# [END import] + + +# [START data_model] +def create_data_model(): + """Stores the data for the problem.""" + data = {} + data['time_matrix'] = [ + [0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7], + [6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14], + [9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9], + [8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16], + [7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14], + [3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8], + [6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5], + [2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10], + [3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6], + [2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5], + [6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4], + [6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10], + [4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8], + [4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6], + [5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2], + [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9], + [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0], + ] + data['time_windows'] = [ + (0, 5), # depot + (7, 12), # 1 + (10, 15), # 2 + (5, 14), # 3 + (5, 13), # 4 + (0, 5), # 5 + (5, 10), # 6 + (0, 10), # 7 + (5, 10), # 8 + (0, 5), # 9 + (10, 16), # 10 + (10, 15), # 11 + (0, 5), # 12 + (5, 10), # 13 + (7, 12), # 14 + (10, 15), # 15 + (5, 15), # 16 + ] + data['num_vehicles'] = 4 + # [START resources_data] + data['vehicle_load_time'] = 5 + data['vehicle_unload_time'] = 5 + data['depot_capacity'] = 2 + # [END resources_data] + data['depot'] = 0 + return data + # [END data_model] + + +# [START solution_printer] +def print_solution(data, manager, routing, assignment): + """Prints assignment on console.""" + print('Objective: {}'.format(assignment.ObjectiveValue())) + time_dimension = routing.GetDimensionOrDie('Time') + total_time = 0 + for vehicle_id in range(data['num_vehicles']): + index = routing.Start(vehicle_id) + plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) + while not routing.IsEnd(index): + time_var = time_dimension.CumulVar(index) + slack_var = time_dimension.SlackVar(index) + plan_output += ' {0} Time({1},{2}) Slack({3},{4})-> '.format( + manager.IndexToNode(index), assignment.Min(time_var), + assignment.Max(time_var), assignment.Min(slack_var), + assignment.Max(slack_var)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + time_var = time_dimension.CumulVar(index) + plan_output += ' {0} Time({1},{2})\n'.format( + manager.IndexToNode(index), assignment.Min(time_var), + assignment.Max(time_var)) + plan_output += 'Time of the route: {}min\n'.format(assignment.Min(time_var)) + print(plan_output) + total_time += assignment.Min(time_var) + print('Total time of all routes: {}min'.format(total_time)) + # [END solution_printer] + + +def main(): + """Solve the VRP with time windows.""" + # Instantiate the data problem. + # [START data] + data = create_data_model() + # [END data] + + # Create the routing index manager. + # [START index_manager] + manager = pywrapcp.RoutingIndexManager( + len(data['time_matrix']), data['num_vehicles'], data['depot']) + # [END index_manager] + + # Create Routing Model. + # [START routing_model] + routing = pywrapcp.RoutingModel(manager) + + # [END routing_model] + + # Create and register a transit callback. + # [START transit_callback] + def time_callback(from_index, to_index): + """Returns the travel time between the two nodes.""" + # Convert from routing variable Index to time matrix NodeIndex. + from_node = manager.IndexToNode(from_index) + to_node = manager.IndexToNode(to_index) + return data['time_matrix'][from_node][to_node] + + transit_callback_index = routing.RegisterTransitCallback(time_callback) + # [END transit_callback] + + # Define cost of each arc. + # [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) + # [END arc_cost] + + # Add Time Windows constraint. + # [START time_windows_constraint] + time = 'Time' + routing.AddDimension( + transit_callback_index, + 60, # allow waiting time + 60, # maximum time per vehicle + False, # Don't force start cumul to zero. + time) + time_dimension = routing.GetDimensionOrDie(time) + # Add time window constraints for each location except depot + # and 'copy' the slack var in the solution object (aka Assignment) to print it + for location_idx, time_window in enumerate(data['time_windows']): + if location_idx == 0: + continue + index = manager.NodeToIndex(location_idx) + time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1]) + routing.AddToAssignment(time_dimension.SlackVar(index)) + # Add time window constraints for each vehicle start node + # and 'copy' the slack var in the solution object (aka Assignment) to print it + for vehicle_id in range(data['num_vehicles']): + index = routing.Start(vehicle_id) + time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0], + data['time_windows'][0][1]) + routing.AddToAssignment(time_dimension.SlackVar(index)) + # [END time_windows_constraint] + + # Add resource constraints at the depot. + # [START depot_load_time] + solver = routing.solver() + intervals = [] + for i in range(data['num_vehicles']): + # Add time windows at start of routes + intervals.append( + solver.FixedDurationIntervalVar( + time_dimension.CumulVar(routing.Start(i)), + data['vehicle_load_time'], 'depot_interval')) + # Add time windows at end of routes. + intervals.append( + solver.FixedDurationIntervalVar( + time_dimension.CumulVar(routing.End(i)), + data['vehicle_unload_time'], 'depot_interval')) + # [END depot_load_time] + + # [START depot_capacity] + depot_usage = [1 for i in range(len(intervals))] + solver.AddConstraint( + solver.Cumulative( + intervals, + depot_usage, + data['depot_capacity'], + 'depot')) + # [END depot_capacity] + + # Instantiate route start and end times to produce feasible times. + # [START depot_start_end_times] + for i in range(data['num_vehicles']): + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.Start(i))) + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.End(i))) + # [END depot_start_end_times] + + # Setting first solution heuristic. + # [START parameters] + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) + # [END parameters] + + # Solve the problem. + # [START solve] + assignment = routing.SolveWithParameters(search_parameters) + # [END solve] + + # Print solution on console. + # [START print_solution] + if assignment: + print_solution(data, manager, routing, assignment) + # [END print_solution] + else: + print('No solution found !') + +if __name__ == '__main__': + main() +# [END program]