/// <summary> /// Solves the current routing problem. /// </summary> private void Solve(int number_of_orders, int number_of_vehicles) { Console.WriteLine("Creating model with " + number_of_orders + " orders and " + number_of_vehicles + " vehicles."); // Finalizing model int number_of_locations = locations_.Length; RoutingModel model = new RoutingModel(number_of_locations, number_of_vehicles, vehicle_starts_, vehicle_ends_); // Setting up dimensions const int big_number = 100000; NodeEvaluator2 manhattan_callback = new Manhattan(locations_, 1); model.AddDimension( manhattan_callback, big_number, big_number, false, "time"); NodeEvaluator2 demand_callback = new Demand(order_demands_); model.AddDimension(demand_callback, 0, vehicle_capacity_, true, "capacity"); // Setting up vehicles NodeEvaluator2[] cost_callbacks = new NodeEvaluator2[number_of_vehicles]; for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { int cost_coefficient = vehicle_cost_coefficients_[vehicle]; NodeEvaluator2 manhattan_cost_callback = new Manhattan(locations_, cost_coefficient); cost_callbacks[vehicle] = manhattan_cost_callback; model.SetVehicleCost(vehicle, manhattan_cost_callback); model.CumulVar(model.End(vehicle), "time").SetMax( vehicle_end_time_[vehicle]); } // Setting up orders for (int order = 0; order < number_of_orders; ++order) { model.CumulVar(order, "time").SetRange(order_time_windows_[order].start_, order_time_windows_[order].end_); int[] orders = { order }; model.AddDisjunction(orders, order_penalties_[order]); } // Solving RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters(); search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.AllUnperformed; Console.WriteLine("Search"); Assignment solution = model.SolveWithParameters(search_parameters); //protect callbacks from the GC GC.KeepAlive(manhattan_callback); GC.KeepAlive(demand_callback); for (int cost_callback_index = 0; cost_callback_index < cost_callbacks.Length; cost_callback_index++) { GC.KeepAlive(cost_callbacks[cost_callback_index]); } if (solution != null) { String output = "Total cost: " + solution.ObjectiveValue() + "\n"; // Dropped orders String dropped = ""; for (int order = 0; order < number_of_orders; ++order) { if (solution.Value(model.NextVar(order)) == order) { dropped += " " + order; } } if (dropped.Length > 0) { output += "Dropped orders:" + dropped + "\n"; } // Routes for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { String route = "Vehicle " + vehicle + ": "; long order = model.Start(vehicle); if (model.IsEnd(solution.Value(model.NextVar(order)))) { route += "Empty"; } else { for (; !model.IsEnd(order); order = solution.Value(model.NextVar(order))) { IntVar local_load = model.CumulVar(order, "capacity"); IntVar local_time = model.CumulVar(order, "time"); route += order + " Load(" + solution.Value(local_load) + ") " + "Time(" + solution.Min(local_time) + ", " + solution.Max(local_time) + ") -> "; } IntVar load = model.CumulVar(order, "capacity"); IntVar time = model.CumulVar(order, "time"); route += order + " Load(" + solution.Value(load) + ") " + "Time(" + solution.Min(time) + ", " + solution.Max(time) + ")"; } output += route + "\n"; } Console.WriteLine(output); } }
/// <summary> /// requires data.SantaIds /// requires data.Visits /// requires data.HomeIndex /// requires data.Unavailable /// requires data.Start /// requires data.End /// </summary> public static OptimizationResult Solve(RoutingData data, long timeLimitMilliseconds, ITimeWindowStrategy strategy) { if (false || data.SantaIds == null || data.Visits == null || data.Unavailable == null || data.SantaStartIndex == null || data.SantaEndIndex == null ) { throw new ArgumentNullException(); } var model = new RoutingModel(data.Visits.Length, data.SantaIds.Length, data.SantaStartIndex, data.SantaEndIndex); // setting up dimensions var maxTime = GetMaxTime(data); var timeCallback = new TimeEvaluator(data); model.AddDimension(timeCallback, 0, maxTime, false, DimensionTime); var lengthCallback = new TimeEvaluator(data); model.AddDimension(lengthCallback, 0, maxTime, true, DimensionLength); // set additional cost of longest day { var dim = model.GetDimensionOrDie(DimensionLength); dim.SetGlobalSpanCostCoefficient(data.Cost.CostLongestDayPerHour); } // dimensions for breaks var breakCallbacks = new List <BreakEvaluator>(); var breakDimensions = new List <string>(); for (int santa = 0; santa < data.NumberOfSantas; santa++) { var maxBreaks = GetNumberOfBreaks(data, santa); if (maxBreaks == 0) { // no breaks continue; } var evaluator = new BreakEvaluator(data, santa); var dimension = GetSantaBreakDimension(santa); model.AddDimension(evaluator, 0, maxBreaks, true, dimension); breakCallbacks.Add(evaluator); breakDimensions.Add(dimension); } // setting up santas (=vehicles) var costCallbacks = new NodeEvaluator2[data.NumberOfSantas]; for (int santa = 0; santa < data.NumberOfSantas; santa++) { // must be a new instance per santa NodeEvaluator2 costCallback = data.Input.IsAdditionalSanta(data.SantaIds[santa]) ? new CostEvaluator(data, data.Cost.CostWorkPerHour + data.Cost.CostAdditionalSantaPerHour, data.Cost.CostAdditionalSanta) : new CostEvaluator(data, data.Cost.CostWorkPerHour, 0); costCallbacks[santa] = costCallback; model.SetVehicleCost(santa, costCallback); // limit time per santa var day = data.GetDayFromSanta(santa); var start = GetDayStart(data, day); var end = GetDayEnd(data, day); model.CumulVar(model.End(santa), DimensionTime).SetRange(start, end); model.CumulVar(model.Start(santa), DimensionTime).SetRange(start, end); // avoid visiting breaks of other santas var breakDimension = GetSantaBreakDimension(santa); foreach (var dimension in breakDimensions.Except(new[] { breakDimension })) { model.CumulVar(model.End(santa), dimension).SetMax(0); } } // setting up visits (=orders) for (int visit = 0; visit < data.NumberOfVisits; ++visit) { var cumulTimeVar = model.CumulVar(visit, DimensionTime); cumulTimeVar.SetRange(data.OverallStart, data.OverallEnd); model.AddDisjunction(new[] { visit }, data.Cost.CostNotVisitedVisit); // add desired / unavailable according to strategy var timeDimension = model.GetDimensionOrDie(DimensionTime); strategy.AddConstraints(data, model, cumulTimeVar, timeDimension, visit); } // Solving var searchParameters = RoutingModel.DefaultSearchParameters(); searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.Automatic; // maybe try AllUnperformed or PathCheapestArc searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.GuidedLocalSearch; searchParameters.TimeLimitMs = timeLimitMilliseconds; var solution = model.SolveWithParameters(searchParameters); // protect callbacks from the GC GC.KeepAlive(timeCallback); GC.KeepAlive(lengthCallback); foreach (var costCallback in costCallbacks) { GC.KeepAlive(costCallback); } foreach (var breakCallback in breakCallbacks) { GC.KeepAlive(breakCallback); } Debug.WriteLine($"obj={solution?.ObjectiveValue()}"); return(CreateResult(data, model, solution)); }