diff --git a/makefiles/Makefile.dotnet.mk b/makefiles/Makefile.dotnet.mk index 20152661f3..1a21fc195a 100644 --- a/makefiles/Makefile.dotnet.mk +++ b/makefiles/Makefile.dotnet.mk @@ -566,6 +566,7 @@ test_dotnet_constraint_solver_samples: $(MAKE) run SOURCE=ortools/constraint_solver/samples/TspCircuitBoard.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/TspDistanceMatrix.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/Vrp.cs + $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpBreaks.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpCapacity.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpDropNodes.cs $(MAKE) run SOURCE=ortools/constraint_solver/samples/VrpGlobalSpan.cs diff --git a/ortools/constraint_solver/samples/VrpBreaks.cs b/ortools/constraint_solver/samples/VrpBreaks.cs new file mode 100644 index 0000000000..c9871cacf0 --- /dev/null +++ b/ortools/constraint_solver/samples/VrpBreaks.cs @@ -0,0 +1,191 @@ +// Copyright 2010-2021 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.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP using time matrix. +/// +public class VrpBreaks +{ + // [START data_model] + class DataModel + { + public long[,] TimeMatrix = { + { 0, 27, 38, 34, 29, 13, 25, 9, 15, 9, 26, 25, 19, 17, 23, 38, 33 }, + { 27, 0, 34, 15, 9, 25, 36, 17, 34, 37, 54, 29, 24, 33, 50, 43, 60 }, + { 38, 34, 0, 49, 43, 25, 13, 40, 23, 37, 20, 63, 58, 56, 39, 77, 37 }, + { 34, 15, 49, 0, 5, 32, 43, 25, 42, 44, 61, 25, 31, 41, 58, 28, 67 }, + { 29, 9, 43, 5, 0, 26, 38, 19, 36, 38, 55, 20, 25, 35, 52, 33, 62 }, + { 13, 25, 25, 32, 26, 0, 11, 15, 9, 12, 29, 38, 33, 31, 25, 52, 35 }, + { 25, 36, 13, 43, 38, 11, 0, 26, 9, 23, 17, 50, 44, 42, 25, 63, 24 }, + { 9, 17, 40, 25, 19, 15, 26, 0, 17, 19, 36, 23, 17, 16, 33, 37, 42 }, + { 15, 34, 23, 42, 36, 9, 9, 17, 0, 13, 19, 40, 34, 33, 16, 54, 25 }, + { 9, 37, 37, 44, 38, 12, 23, 19, 13, 0, 17, 26, 21, 19, 13, 40, 23 }, + { 26, 54, 20, 61, 55, 29, 17, 36, 19, 17, 0, 43, 38, 36, 19, 57, 17 }, + { 25, 29, 63, 25, 20, 38, 50, 23, 40, 26, 43, 0, 5, 15, 32, 13, 42 }, + { 19, 24, 58, 31, 25, 33, 44, 17, 34, 21, 38, 5, 0, 9, 26, 19, 36 }, + { 17, 33, 56, 41, 35, 31, 42, 16, 33, 19, 36, 15, 9, 0, 17, 21, 26 }, + { 23, 50, 39, 58, 52, 25, 25, 33, 16, 13, 19, 32, 26, 17, 0, 38, 9 }, + { 38, 43, 77, 28, 33, 52, 63, 37, 54, 40, 57, 13, 19, 21, 38, 0, 39 }, + { 33, 60, 37, 67, 62, 35, 24, 42, 25, 23, 17, 42, 36, 26, 9, 39, 0 }, + }; + public long[] ServiceTime = { + 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + }; + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution(in RoutingModel routing, in RoutingIndexManager manager, in Assignment solution) + { + Console.WriteLine($"Objective {solution.ObjectiveValue()}:"); + + Console.WriteLine("Breaks:"); + AssignmentIntervalContainer intervals = solution.IntervalVarContainer(); + for (int i = 0; i < intervals.Size(); ++i) + { + IntervalVarElement breakInterval = intervals.Element(i); + if (breakInterval.PerformedValue() == 1) + { + Console.WriteLine($"{breakInterval.Var().Name()} " + breakInterval.ToString()); + } + else + { + Console.WriteLine($"{breakInterval.Var().Name()}: Unperformed"); + } + } + + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + long totalTime = 0; + for (int i = 0; i < manager.GetNumberOfVehicles(); ++i) + { + Console.WriteLine($"Route for Vehicle {i}:"); + var index = routing.Start(i); + while (routing.IsEnd(index) == false) + { + IntVar timeVar = timeDimension.CumulVar(index); + Console.Write($"{manager.IndexToNode((int)index)} Time({solution.Value(timeVar)}) -> "); + index = solution.Value(routing.NextVar(index)); + } + IntVar endTimeVar = timeDimension.CumulVar(index); + Console.WriteLine($"{manager.IndexToNode((int)index)} Time({solution.Value(endTimeVar)})"); + Console.WriteLine($"Time of the route: {solution.Value(endTimeVar)}min"); + totalTime += solution.Value(endTimeVar); + } + Console.WriteLine($"Total time of all routes: {totalTime}min"); + Console.WriteLine($"Problem solved in {routing.solver().WallTime()}ms"); + } + // [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 time matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.TimeMatrix[fromNode, toNode] + data.ServiceTime[fromNode]; + }); + // [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, 10, 180, + true, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + timeDimension.SetGlobalSpanCostCoefficient(10); + // [END time_constraint] + + // Add Breaks + long[] serviceTimes = new long[routing.Size()]; + for (int index = 0; index < routing.Size(); index++) + { + int node = manager.IndexToNode(index); + serviceTimes[index] = data.ServiceTime[node]; + } + + Solver solver = routing.solver(); + for (int vehicle = 0; vehicle < manager.GetNumberOfVehicles(); ++vehicle) + { + List breakIntervals = new List(); + IntervalVar break_interval = solver.MakeFixedDurationIntervalVar(50, // start min + 60, // start max + 10, // duration: 10min + false, // optional: no + "Break for vehicle " + vehicle); + breakIntervals.Add(break_interval); + + timeDimension.SetBreakIntervalsOfVehicle(breakIntervals.ToArray(), vehicle, serviceTimes); + } + + // 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] + if (solution != null) + { + PrintSolution(routing, manager, solution); + } + else + { + Console.WriteLine("Solution not found."); + } + // [END print_solution] + } +} +// [END program] diff --git a/ortools/constraint_solver/samples/VrpBreaks.csproj b/ortools/constraint_solver/samples/VrpBreaks.csproj new file mode 100644 index 0000000000..7af6cafb0f --- /dev/null +++ b/ortools/constraint_solver/samples/VrpBreaks.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.VrpBreaks + true + + + + full + true + true + + + + + + +