private void AddTimeWindowConstrains(WorkData data, RoutingIndexManager manager, RoutingModel routing) { // Create and register a transit callback int transitCallbackIndex = routing.RegisterTransitCallback( (long fromIndex, long toIndex) => { int fromNode = manager.IndexToNode(fromIndex); int toNode = manager.IndexToNode(toIndex); return(data.DurationMatrix[fromNode, toNode] + data.ServiceTimes[fromNode]); }); routing.AddDimension(transitCallbackIndex, 120, MAX_VEHICLE_ROUTING_TIME, false, "Time"); // Add time window constraints for each location except depot RoutingDimension timeDimension = routing.GetDimensionOrDie("Time"); 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]); } // Add time window constraints for each vehicle start node for (int i = 0; i < data.VehiclesAmount; ++i) { long index = routing.Start(i); timeDimension.CumulVar(index).SetRange(data.TimeWindows[0][0], data.TimeWindows[0][1]); routing.AddVariableMinimizedByFinalizer(timeDimension.CumulVar(routing.Start(i))); routing.AddVariableMinimizedByFinalizer(timeDimension.CumulVar(routing.End(i))); } }
static void TimeWindowInit(Day day, RoutingModel routing, RoutingIndexManager manager) { RoutingDimension timeDimension = routing.GetMutableDimension("Time"); timeDimension.SetGlobalSpanCostCoefficient(100); // Add time window constraints for each location except depot. for (int i = 1; i < day.TimeWindows.GetLength(0); i++) { long index = manager.NodeToIndex(i); timeDimension.CumulVar(index).SetRange( day.TimeWindows[i, 0], day.TimeWindows[i, 1]); } // Add time window constraints for each vehicle start node. for (int i = 0; i < day.Vehicles.Count; i++) { long index = routing.Start(i); timeDimension.CumulVar(index).SetRange( day.TimeWindows[0, 0], day.TimeWindows[0, 1]); routing.AddVariableMinimizedByFinalizer( timeDimension.CumulVar(routing.Start(i))); routing.AddVariableMinimizedByFinalizer( timeDimension.CumulVar(routing.End(i))); } }
public void PrintSolutionUsingRoutingVars(Assignment solution) { if (solution != null) { var capacityDimension = RoutingModel.GetMutableDimension("Capacity"); var timeDimension = RoutingModel.GetMutableDimension("Time"); // Inspect solution. long totalTime = 0; long totalLoad = 0; for (int i = 0; i < DataModel.VehicleCapacities.Length; ++i) { Console.WriteLine("Route for Vehicle {0}:", i); var index = RoutingModel.Start(i); long routeLoad = 0; while (RoutingModel.IsEnd(index) == false) { var capVar = capacityDimension.CumulVar(index); var capTransit = capacityDimension.TransitVar(index); var timeVar = timeDimension.CumulVar(index); var slackVar = timeDimension.SlackVar(index); var transitVar = timeDimension.TransitVar(index); Console.Write("{0} Time: C({1},{2}) S({3},{4}) T({5}) Capacity: C({6}) T({7})-> ", RoutingIndexManager.IndexToNode(index), solution.Min(timeVar), solution.Max(timeVar), solution.Min(slackVar), solution.Max(slackVar), solution.Value(transitVar), solution.Value(capVar), solution.Value(capTransit)); index = solution.Value(RoutingModel.NextVar(index)); } var endTimeVar = timeDimension.CumulVar(index); var endCapVar = capacityDimension.CumulVar(index); routeLoad += solution.Value(endCapVar); totalLoad += routeLoad; Console.WriteLine("{0} Time: C({1},{2}) Capacity: C({3})", RoutingIndexManager.IndexToNode(index), solution.Min(endTimeVar), solution.Max(endTimeVar), solution.Value(endCapVar)); Console.WriteLine("Time of the route: {0}min", solution.Min(endTimeVar)); Console.WriteLine("Route load: " + routeLoad); totalTime += solution.Min(endTimeVar); } Console.WriteLine("Total time of all routes: {0}min", totalTime); Console.WriteLine("Total Load of all routes: " + totalLoad + "min"); Console.WriteLine("Avg route Total Time:{0}", totalTime / DataModel.VehicleCapacities.Length); } else { Console.WriteLine("No sol found upperbound:" + MaximumDeliveryDelayTime); } }
static void Solve(int size, int forbidden, int seed) { RoutingIndexManager manager = new RoutingIndexManager(size, 1, 0); RoutingModel routing = new RoutingModel(manager); // Setting the cost function. // Put a permanent callback to the distance accessor here. The callback // has the following signature: ResultCallback2<int64_t, int64_t, int64_t>. // The two arguments are the from and to node inidices. RandomManhattan distances = new RandomManhattan(manager, size, seed); routing.SetArcCostEvaluatorOfAllVehicles(routing.RegisterTransitCallback(distances.Call)); // Forbid node connections (randomly). Random randomizer = new Random(); long forbidden_connections = 0; while (forbidden_connections < forbidden) { long from = randomizer.Next(size - 1); long to = randomizer.Next(size - 1) + 1; if (routing.NextVar(from).Contains(to)) { Console.WriteLine("Forbidding connection {0} -> {1}", from, to); routing.NextVar(from).RemoveValue(to); ++forbidden_connections; } } // Add dummy dimension to test API. routing.AddDimension(routing.RegisterUnaryTransitCallback((long index) => { return(1); }), size + 1, size + 1, true, "dummy"); // Solve, returns a solution if any (owned by RoutingModel). RoutingSearchParameters search_parameters = operations_research_constraint_solver.DefaultRoutingSearchParameters(); // Setting first solution heuristic (cheapest addition). search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; Assignment solution = routing.SolveWithParameters(search_parameters); Console.WriteLine("Status = {0}", routing.GetStatus()); if (solution != null) { // Solution cost. Console.WriteLine("Cost = {0}", solution.ObjectiveValue()); // Inspect solution. // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 int route_number = 0; for (long node = routing.Start(route_number); !routing.IsEnd(node); node = solution.Value(routing.NextVar(node))) { Console.Write("{0} -> ", node); } Console.WriteLine("0"); } }
static void Solve(int size, int forbidden, int seed) { RoutingModel routing = new RoutingModel(size, 1); // Setting first solution heuristic (cheapest addition). routing.SetFirstSolutionStrategy(RoutingModel.ROUTING_PATH_CHEAPEST_ARC); // Setting the cost function. // Put a permanent callback to the distance accessor here. The callback // has the following signature: ResultCallback2<int64, int64, int64>. // The two arguments are the from and to node inidices. RandomManhattan distances = new RandomManhattan(size, seed); routing.SetCost(distances); // Forbid node connections (randomly). Random randomizer = new Random(); long forbidden_connections = 0; while (forbidden_connections < forbidden) { long from = randomizer.Next(size - 1); long to = randomizer.Next(size - 1) + 1; if (routing.NextVar(from).Contains(to)) { Console.WriteLine("Forbidding connection {0} -> {1}", from, to); routing.NextVar(from).RemoveValue(to); ++forbidden_connections; } } // Add dummy dimension to test API. routing.AddDimension(new ConstantCallback(), size + 1, size + 1, true, "dummy"); // Solve, returns a solution if any (owned by RoutingModel). Assignment solution = routing.Solve(); if (solution != null) { // Solution cost. Console.WriteLine("Cost = {0}", solution.ObjectiveValue()); // Inspect solution. // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 int route_number = 0; for (long node = routing.Start(route_number); !routing.IsEnd(node); node = solution.Value(routing.NextVar(node))) { Console.Write("{0} -> ", node); } Console.WriteLine("0"); } }
public void PrintToConsole( Day day, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { Console.WriteLine("Day " + day.DayNum); if (day.Locations.Count != 0) { // Display dropped nodes. string droppedNodes = "Dropped nodes:"; for (int index = 0; index < routing.Size(); ++index) { if (routing.IsStart(index) || routing.IsEnd(index)) { continue; } if (solution.Value(routing.NextVar(index)) == index) { droppedNodes += " " + manager.IndexToNode(index); } } Console.WriteLine("{0}", droppedNodes); // Inspect solution. for (int i = 0; i < day.Vehicles.Count; i++) { Console.WriteLine("Route for Vehicle {0}:", i); long routeDuration = 0; var index = routing.Start(i); while (routing.IsEnd(index) == false) { Console.Write("{0} -> ", manager.IndexToNode((int)index)); var previousIndex = index; index = solution.Value(routing.NextVar(index)); routeDuration += routing.GetArcCostForVehicle(previousIndex, index, 0); } Console.WriteLine("{0}", manager.IndexToNode((int)index)); Console.WriteLine("Duration of the route: {0}mins", routeDuration); } Console.WriteLine("Minimum duration of the routes: {0}mins", day.MinDur); Console.WriteLine("Maximum duration of the routes: {0}mins", day.MaxDur); } else { Console.WriteLine("This day is holiday."); } }
static int[] GetOrder(RoutingModel routing, RoutingIndexManager manager, Assignment solution) { List <int> order = new List <int>(); var index = routing.Start(0); while (routing.IsEnd(index) == false) { order.Add(manager.IndexToNode((int)index)); index = solution.Value(routing.NextVar(index)); } return(order.ToArray()); }
/// <summary> /// Returns the Routes from the solution. /// </summary> /// <param name="data"></param> /// <param name="model"></param> /// <param name="solution"></param> /// <returns></returns> private static Route[] GetRoutes(RoutingData data, RoutingModel model, Assignment solution) { if (solution == null) { return(new Route[0]); } // Routes var routes = new List <Route>(); for (int santa = 0; santa < data.NumberOfSantas; ++santa) { long visit = model.Start(santa); if (model.IsEnd(solution.Value(model.NextVar(visit)))) { // empty } else { var waypoints = new List <Waypoint>(); void AddWaypoint(long v) { var startTime = solution.Value(model.CumulVar(v, DimensionTime)); var visitId = (v >= data.Visits.Length || v < 0) ? Constants.VisitIdHome : data.Visits[v].Id; waypoints.Add(new Waypoint { StartTime = (int)startTime, VisitId = visitId, }); } for (; !model.IsEnd(visit); visit = solution.Value(model.NextVar(visit))) { AddWaypoint(visit); } // add end { AddWaypoint(visit); } routes.Add(new Route { SantaId = data.SantaIds[santa], Waypoints = waypoints.ToArray(), }); } } return(routes.ToArray()); }
private void Distancia() { long routeDistance = 0; RoutingSearchParameters searchParameters = operations_research_constraint_solver.DefaultRoutingSearchParameters(); searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; DataModel data = new DataModel(Distancias, 1, 0); RoutingIndexManager manager = new RoutingIndexManager( data.DistanceMatrix.GetLength(0), data.VehicleNumber, data.Depot); RoutingModel routing = new RoutingModel(manager); 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.DistanceMatrix[fromNode, toNode]); }); routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); Assignment solution = routing.SolveWithParameters(searchParameters); var index = routing.Start(0); int indice = 1; string CadenaR = ""; string c = ""; while (routing.IsEnd(index) == false) { //Recorrido[indice] = manager.IndexToNode((int)index); c = manager.IndexToNode((int)index).ToString(); CadenaR = CadenaR + " - " + c; var previousIndex = index; index = solution.Value(routing.NextVar(index)); routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); indice++; } //Recorrido[indice] = manager.IndexToNode((int)index); c = manager.IndexToNode((int)index).ToString(); CadenaR = CadenaR + " - " + c; Km = routeDistance; CadenaRecorrido = CadenaR; }
public override void Solve() { var pparser = new Pparser(FpatIn); int n, l; pparser.Fetch(out n, out l); var rgnode = pparser.FetchN <string>(n); rgnode.Insert(0, "X"); var model = new RoutingModel(n, 1); model.SetFirstSolutionStrategy(RoutingModel.ROUTING_GLOBAL_CHEAPEST_ARC); //model.SetMetaheuristic(RoutingModel.ROUTING_TABU_SEARCH); model.SetCost(new NodeEval(rgnode.ToArray())); model.SetDepot(0); for (int i = 0; i < n; i++) { var varI = model.NextVar(i); for (int j = 0; j < n; j++) { if (i == j) { continue; } if (!NodeEval.FMatch(rgnode[i], rgnode[j])) { varI.RemoveValue(j); } } } Console.WriteLine("solving"); Assignment solution = model.Solve(); model.UpdateTimeLimit(1000 * 60 * 3); if (solution != null) { // Solution cost. Console.WriteLine("Cost = {0}", solution.ObjectiveValue()); for (var inode = (int)model.Start(0); !model.IsEnd(inode); inode = (int)solution.Value(model.NextVar(inode))) { Console.WriteLine(rgnode[inode]); } Console.WriteLine("0"); } }
private IList <Summary> GetSummaries(FileInput input, RoutingIndexManager manager, RoutingModel routing, Assignment solution) { int vehiclesAmount = input.Vehicles.Count(); IDictionary <int, string> vehiclesNames = input.Vehicles.ToDictionary(k => k.Id - 1, v => v.Name); List <Summary> summaries = new List <Summary>(); RoutingDimension capacityDimension = routing.GetDimensionOrDie("Capacity"); RoutingDimension timeDimension = routing.GetMutableDimension("Time"); for (int vehicleIndex = 0; vehicleIndex < vehiclesAmount; ++vehicleIndex) { long load = 0; long routeDistance = 0; long routeIndex = routing.Start(vehicleIndex); int numberOfVisits = 0; while (routing.IsEnd(routeIndex) == false) { long previousVehicleIndex = routeIndex; load += solution.Value(capacityDimension.CumulVar(routeIndex)); routeDistance += routing.GetArcCostForVehicle(previousVehicleIndex, routeIndex, 0); ++numberOfVisits; routeIndex = solution.Value(routing.NextVar(routeIndex)); } load += solution.Value(capacityDimension.CumulVar(routeIndex)); long vehicleSpentTime = solution.Min(timeDimension.CumulVar(routeIndex)); summaries.Add(new Summary { VehicleName = vehiclesNames[vehicleIndex], Load = (int)load, Distance = (int)routeDistance, Time = (int)vehicleSpentTime, NumberOfVisits = numberOfVisits }); } return(summaries); }
private List <Destination> ExtractWaybillsFromSolution(Assignment solution, RoutingModel model, List <Destination> destinations) { var result = new List <Destination>(); var index = model.Start(0); while (!model.IsEnd(index)) { if (index < destinations.Count) { var currentDestination = destinations[(int)index]; result.Add(currentDestination); } index = solution.Value(model.NextVar(index)); } return(result); }
private IList <Totals> GetTotals(FileInput input, RoutingIndexManager manager, RoutingModel routing, Assignment solution) { int vehiclesAmount = input.Vehicles.Count(); List <Totals> totals = new List <Totals>(); RoutingDimension capacityDimension = routing.GetDimensionOrDie("Capacity"); RoutingDimension timeDimension = routing.GetMutableDimension("Time"); long totalLoad = 0; long totalTime = 0; long totalDistance = 0; for (int vehicleIndex = 0; vehicleIndex < vehiclesAmount; ++vehicleIndex) { long load = 0; long routeDistance = 0; long routeIndex = routing.Start(vehicleIndex); while (routing.IsEnd(routeIndex) == false) { long previousRouteIndex = routeIndex; load += solution.Value(capacityDimension.CumulVar(routeIndex)); routeDistance += routing.GetArcCostForVehicle(previousRouteIndex, routeIndex, 0); routeIndex = solution.Value(routing.NextVar(routeIndex)); } load += solution.Value(capacityDimension.CumulVar(routeIndex)); totalLoad += load; totalTime += solution.Min(timeDimension.CumulVar(routeIndex)); totalDistance += routeDistance; } totals.Add(new Totals { Load = (int)totalLoad, Distance = (int)totalDistance, Time = (int)totalTime }); return(totals); }
/// <summary> /// Print the solution. /// </summary> public static int[] Solve() { int[] result = new int[200]; int cnt = 0; // Create Routing Index Manager RoutingIndexManager manager = new RoutingIndexManager(Ranks, VehicleNumber, Depot); // Create Routing Model. RoutingModel routing = new RoutingModel(manager); 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((long)distMat[fromNode, toNode]); } ); routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); // Setting first solution heuristic. RoutingSearchParameters searchParameters = operations_research_constraint_solver.DefaultRoutingSearchParameters(); searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; // Solve the problem. Assignment solution = routing.SolveWithParameters(searchParameters); // Inspect solution. var index = routing.Start(0); while (routing.IsEnd(index) == false) { result[cnt++] = Convert.ToInt32(index); var previousIndex = index; index = solution.Value(routing.NextVar(index)); } return(result); }
public string[] Solve(string[] nodes) { var routing = new RoutingModel(nodes.Length, 1, 0); routing.SetCost(new HaversineDistanceEvaluator(nodes, _maps)); var parameters = RoutingModel.DefaultSearchParameters(); parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; var solution = routing.SolveWithParameters(parameters); int routeNumber = 0; var results = new List <string>(); for (long node = routing.Start(routeNumber); !routing.IsEnd(node); node = solution.Value(routing.NextVar(node))) { results.Add(nodes[routing.IndexToNode(node)]); } return(results.ToArray()); }
/// <summary> /// Print the solution. /// </summary> static string PrintSolution(RoutingModel routing, RoutingIndexManager manager, Assignment solution) { StringBuilder sb = new StringBuilder(); sb.AppendFormat("Objective: {0} miles\r\n", solution.ObjectiveValue()); // Inspect solution. sb.AppendFormat("Route:"); long routeDistance = 0; var index = routing.Start(0); while (routing.IsEnd(index) == false) { sb.AppendFormat("{0} -> ", manager.IndexToNode((int)index)); var previousIndex = index; index = solution.Value(routing.NextVar(index)); routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); } sb.AppendFormat("{0}\r\n", manager.IndexToNode((int)index)); sb.AppendFormat("Route distance: {0}miles\r\n", routeDistance); return(sb.ToString()); }
private IList <Itineraries> GetItineraries(FileInput input, RoutingIndexManager manager, RoutingModel routing, Assignment solution) { int vehiclesAmount = input.Vehicles.Count(); DateTime minDate = input.Locations.Select(l => l.From).Min(); IDictionary <int, string> vehiclesNames = input.Vehicles.ToDictionary(k => k.Id - 1, v => v.Name); List <Itineraries> itineraries = new List <Itineraries>(); RoutingDimension capacityDimension = routing.GetDimensionOrDie("Capacity"); RoutingDimension timeDimension = routing.GetMutableDimension("Time"); for (int vehicleIndex = 0; vehicleIndex < vehiclesAmount; ++vehicleIndex) { long load = 0; long routeIndex = routing.Start(vehicleIndex); while (routing.IsEnd(routeIndex) == false) { long previousRouteIndex = routeIndex; load += solution.Value(capacityDimension.CumulVar(routeIndex)); long distance = routing.GetArcCostForVehicle(previousRouteIndex, routeIndex, 0); IntVar timeVar = timeDimension.CumulVar(routeIndex); itineraries.Add(new Itineraries { VehicleName = vehiclesNames[vehicleIndex], Load = (int)load, Distance = (int)distance, From = minDate.AddMinutes(solution.Min(timeVar)), To = minDate.AddMinutes(solution.Max(timeVar)) }); routeIndex = solution.Value(routing.NextVar(routeIndex)); } } return(itineraries); }
/// <summary> /// Print the solution. /// </summary> static void PrintSolution(DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { long totalDistance = 0; for (int i = 0; i < data.VehicleNumber; ++i) { Console.WriteLine("Route for Vehicle {0}:", i); long routeDistance = 0; var index = routing.Start(i); while (routing.IsEnd(index) == false) { Console.Write("{0} -> ", manager.IndexToNode((int)index)); var previousIndex = index; index = solution.Value(routing.NextVar(index)); routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); } Console.WriteLine("{0}", manager.IndexToNode((int)index)); Console.WriteLine("Distance of the route: {0}m", routeDistance); totalDistance += routeDistance; } Console.WriteLine("Total Distance of all routes: {0}m", totalDistance); }
void ShowOnMap(Day day, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { List <List <int> > routes = new List <List <int> >(); for (int i = 0; i < day.Vehicles.Count; ++i) { var index = routing.Start(i); List <int> route = new List <int>(); while (routing.IsEnd(index) == false) { route.Add(manager.IndexToNode((int)index)); index = solution.Value(routing.NextVar(index)); } route.Add(manager.IndexToNode((int)index)); routes.Add(route); } for (int i = 0; i < routes.Count; i++) { string url = "https://www.google.com.tr/maps/dir/"; foreach (var item in routes[i]) { url += day.Addresses[item].Replace('+', ',') + "/"; } //url += data.addresses[data.Depot]; Process process = new Process(); process.StartInfo.UseShellExecute = true; process.StartInfo.FileName = "chrome"; process.StartInfo.Arguments = url; process.Start(); } }
/// <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> // Получаем предложение по оптимальному расположению адресов в указанном маршруте. // Рачет идет с учетом окон доставки. Но естественно без любых ограничений по весу и прочему. // </summary> // <returns>Предолженый маршрут</returns> // <param name="route">Первоначальный маршрутный лист, чтобы взять адреса.</param> public ProposedRoute RebuidOneRoute(RouteList route) { var trip = new PossibleTrip(route); logger.Info("Подготавливаем заказы..."); PerformanceHelper.StartMeasurement($"Строим маршрут"); List <CalculatedOrder> calculatedOrders = new List <CalculatedOrder>(); foreach (var address in route.Addresses) { if (address.Order.DeliveryPoint.Longitude == null || address.Order.DeliveryPoint.Latitude == null) { continue; } calculatedOrders.Add(new CalculatedOrder(address.Order, null)); } Nodes = calculatedOrders.ToArray(); distanceCalculator = new ExtDistanceCalculator(DistanceProvider.Osrm, Nodes.Select(x => x.Order.DeliveryPoint).ToArray(), StatisticsTxtAction); PerformanceHelper.AddTimePoint(logger, $"Подготовка заказов"); logger.Info("Создаем модель..."); RoutingModel routing = new RoutingModel(Nodes.Length + 1, 1, 0); int horizon = 24 * 3600; routing.AddDimension(new CallbackTime(Nodes, trip, distanceCalculator), 3 * 3600, horizon, false, "Time"); var time_dimension = routing.GetDimensionOrDie("Time"); var cumulTimeOnEnd = routing.CumulVar(routing.End(0), "Time"); var cumulTimeOnBegin = routing.CumulVar(routing.Start(0), "Time"); if (route.Shift != null) { var shift = route.Shift; cumulTimeOnEnd.SetMax((long)shift.EndTime.TotalSeconds); cumulTimeOnBegin.SetMin((long)shift.StartTime.TotalSeconds); } routing.SetArcCostEvaluatorOfVehicle(new CallbackDistance(Nodes, distanceCalculator), 0); for (int ix = 0; ix < Nodes.Length; ix++) { var startWindow = Nodes[ix].Order.DeliverySchedule.From.TotalSeconds; var endWindow = Nodes[ix].Order.DeliverySchedule.To.TotalSeconds - trip.Driver.TimeCorrection(Nodes[ix].Order.CalculateTimeOnPoint(route.Forwarder != null)); if (endWindow < startWindow) { logger.Warn("Время разгрузки на точке, не помещается в диапазон времени доставки. {0}-{1}", Nodes[ix].Order.DeliverySchedule.From, Nodes[ix].Order.DeliverySchedule.To); endWindow = startWindow; } time_dimension.CumulVar(ix + 1).SetRange((long)startWindow, (long)endWindow); routing.AddDisjunction(new int[] { ix + 1 }, MaxDistanceAddressPenalty); } RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters(); search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion; search_parameters.TimeLimitMs = MaxTimeSeconds * 1000; //search_parameters.FingerprintArcCostEvaluators = true; //search_parameters.OptimizationStep = 100; var solver = routing.solver(); PerformanceHelper.AddTimePoint(logger, $"Настроили оптимизацию"); logger.Info("Закрываем модель..."); logger.Info("Рассчет расстояний между точками..."); routing.CloseModelWithParameters(search_parameters); distanceCalculator.FlushCache(); var lastSolution = solver.MakeLastSolutionCollector(); lastSolution.AddObjective(routing.CostVar()); routing.AddSearchMonitor(lastSolution); routing.AddSearchMonitor(new CallbackMonitor(solver, StatisticsTxtAction, lastSolution)); PerformanceHelper.AddTimePoint(logger, $"Закрыли модель"); logger.Info("Поиск решения..."); Assignment solution = routing.SolveWithParameters(search_parameters); PerformanceHelper.AddTimePoint(logger, $"Получили решение."); logger.Info("Готово. Заполняем."); Console.WriteLine("Status = {0}", routing.Status()); ProposedRoute proposedRoute = null; if (solution != null) { // Solution cost. Console.WriteLine("Cost = {0}", solution.ObjectiveValue()); time_dimension = routing.GetDimensionOrDie("Time"); int route_number = 0; proposedRoute = new ProposedRoute(null); long first_node = routing.Start(route_number); long second_node = solution.Value(routing.NextVar(first_node)); // Пропускаем первый узел, так как это наша база. proposedRoute.RouteCost = routing.GetCost(first_node, second_node, route_number); while (!routing.IsEnd(second_node)) { var time_var = time_dimension.CumulVar(second_node); var rPoint = new ProposedRoutePoint( TimeSpan.FromSeconds(solution.Min(time_var)), TimeSpan.FromSeconds(solution.Max(time_var)), Nodes[second_node - 1].Order ); rPoint.DebugMaxMin = string.Format("\n({0},{1})[{3}-{4}]-{2}", new DateTime().AddSeconds(solution.Min(time_var)).ToShortTimeString(), new DateTime().AddSeconds(solution.Max(time_var)).ToShortTimeString(), second_node, rPoint.Order.DeliverySchedule.From.ToString("hh\\:mm"), rPoint.Order.DeliverySchedule.To.ToString("hh\\:mm") ); proposedRoute.Orders.Add(rPoint); first_node = second_node; second_node = solution.Value(routing.NextVar(first_node)); proposedRoute.RouteCost += routing.GetCost(first_node, second_node, route_number); } } PerformanceHelper.Main.PrintAllPoints(logger); if (distanceCalculator.ErrorWays.Count > 0) { logger.Debug("Ошибок получения расстояний {0}", distanceCalculator.ErrorWays.Count); var uniqueFrom = distanceCalculator.ErrorWays.Select(x => x.FromHash).Distinct().ToList(); var uniqueTo = distanceCalculator.ErrorWays.Select(x => x.ToHash).Distinct().ToList(); logger.Debug("Уникальных точек: отправки = {0}, прибытия = {1}", uniqueFrom.Count, uniqueTo.Count); logger.Debug("Проблемные точки отправки:\n{0}", string.Join("; ", distanceCalculator.ErrorWays .GroupBy(x => x.FromHash) .Where(x => x.Count() > (uniqueTo.Count / 2)) .Select(x => CachedDistance.GetText(x.Key))) ); logger.Debug("Проблемные точки прибытия:\n{0}", string.Join("; ", distanceCalculator.ErrorWays .GroupBy(x => x.ToHash) .Where(x => x.Count() > (uniqueFrom.Count / 2)) .Select(x => CachedDistance.GetText(x.Key))) ); } distanceCalculator.Dispose(); return(proposedRoute); }
// <summary> // Метод создаем маршруты на день основываясь на данных всесенных в поля <c>Routes</c>, <c>Orders</c>, // <c>Drivers</c> и <c>Forwarders</c>. // </summary> public void CreateRoutes(TimeSpan drvStartTime, TimeSpan drvEndTime) { WarningMessages.Clear(); ProposedRoutes.Clear(); //Очищаем сразу, так как можем выйти из метода ранее. logger.Info("Подготавливаем заказы..."); PerformanceHelper.StartMeasurement($"Строим оптимальные маршруты"); // Создаем список поездок всех водителей. Тут перебираем всех водителей с машинами // и создаем поездки для них, в зависимости от выбранного режима работы. var trips = Drivers.Where(x => x.Car != null) .OrderBy(x => x.PriorityAtDay) .SelectMany(drv => drv.DaySchedule != null ? drv.DaySchedule.Shifts.Where(s => s.StartTime >= drvStartTime && s.StartTime < drvEndTime) .Select(shift => new PossibleTrip(drv, shift)) : new[] { new PossibleTrip(drv, null) } ) .ToList(); // Стыкуем уже созданные маршрутные листы с возможными поездками, на основании водителя и смены. // Если уже созданный маршрут не найден в поездках, то создаем поездку для него. foreach (var existRoute in Routes) { var trip = trips.FirstOrDefault(x => x.Driver == existRoute.Driver && x.Shift == existRoute.Shift); if (trip != null) { trip.OldRoute = existRoute; } else { trips.Add(new PossibleTrip(existRoute)); } //Проверяем все ли заказы из МЛ присутствуют в списке заказов. Если их нет. Добавляем. foreach (var address in existRoute.Addresses) { if (Orders.All(x => x.Id != address.Order.Id)) { Orders.Add(address.Order); } } } var possibleRoutes = trips.ToArray(); if (!possibleRoutes.Any()) { AddWarning("Для построения маршрутов, нет водителей."); return; } TestCars(possibleRoutes); var areas = UoW.GetAll <District>().Where(x => x.DistrictsSet.Status == DistrictsSetStatus.Active).ToList(); List <District> unusedDistricts = new List <District>(); List <CalculatedOrder> calculatedOrders = new List <CalculatedOrder>(); // Перебираем все заказы, исключаем те которые без координат, определяем для каждого заказа район // на основании координат. И создавая экземпляр <c>CalculatedOrder</c>, происходит подсчет сумарной // информации о заказе. Всего бутылей, вес и прочее. foreach (var order in Orders) { if (order.DeliveryPoint.Longitude == null || order.DeliveryPoint.Latitude == null) { continue; } var point = new Point((double)order.DeliveryPoint.Latitude.Value, (double)order.DeliveryPoint.Longitude.Value); var area = areas.Find(x => x.DistrictBorder.Contains(point)); if (area != null) { var oldRoute = Routes.FirstOrDefault(r => r.Addresses.Any(a => a.Order.Id == order.Id)); if (oldRoute != null) { calculatedOrders.Add(new CalculatedOrder(order, area, false, oldRoute)); } else if (possibleRoutes.SelectMany(x => x.Districts).Any(x => x.District.Id == area.Id)) { var cOrder = new CalculatedOrder(order, area); //if(possibleRoutes.Any(r => r.GeographicGroup.Id == cOrder.ShippingBase.Id))//убрать, если в автоформировании должны учавствовать заказы из всех частей города вне зависимости от того какие части города выбраны в диалоге calculatedOrders.Add(cOrder); } else if (!unusedDistricts.Contains(area)) { unusedDistricts.Add(area); } } } Nodes = calculatedOrders.ToArray(); if (unusedDistricts.Any()) { AddWarning("Районы без водителей: {0}", string.Join(", ", unusedDistricts.Select(x => x.DistrictName))); } // Создаем калькулятор расчета расстояний. Он сразу запрашивает уже имеющиеся расстояния из кеша // и в фоновом режиме начинает считать недостающую матрицу. distanceCalculator = new ExtDistanceCalculator(DistanceProvider.Osrm, Nodes.Select(x => x.Order.DeliveryPoint).ToArray(), StatisticsTxtAction); logger.Info("Развозка по {0} районам.", calculatedOrders.Select(x => x.District).Distinct().Count()); PerformanceHelper.AddTimePoint(logger, $"Подготовка заказов"); // Пред запуском оптимизации мы должны создать модель и внести в нее все необходимые данные. logger.Info("Создаем модель..."); RoutingModel routing = new RoutingModel(Nodes.Length + 1, possibleRoutes.Length, 0); // Создаем измерение со временем на маршруте. // <c>horizon</c> - ограничивает максимально допустимое значение диапазона, чтобы не уйти за границы суток; // <c>maxWaitTime</c> - Максимальное время ожидания водителя. То есть водитель закончил разгрузку следующий // адрес в маршруте у него не должен быть позже чем на 4 часа ожидания. int horizon = 24 * 3600; int maxWaitTime = 4 * 3600; var timeEvaluators = possibleRoutes.Select(x => new CallbackTime(Nodes, x, distanceCalculator)).ToArray(); routing.AddDimensionWithVehicleTransits(timeEvaluators, maxWaitTime, horizon, false, "Time"); var time_dimension = routing.GetDimensionOrDie("Time"); // Ниже заполняем все измерения для учета бутылей, веса, адресов, объема. var bottlesCapacity = possibleRoutes.Select(x => (long)x.Car.MaxBottles).ToArray(); routing.AddDimensionWithVehicleCapacity(new CallbackBottles(Nodes), 0, bottlesCapacity, true, "Bottles"); var weightCapacity = possibleRoutes.Select(x => (long)x.Car.MaxWeight).ToArray(); routing.AddDimensionWithVehicleCapacity(new CallbackWeight(Nodes), 0, weightCapacity, true, "Weight"); var volumeCapacity = possibleRoutes.Select(x => (long)(x.Car.MaxVolume * 1000)).ToArray(); routing.AddDimensionWithVehicleCapacity(new CallbackVolume(Nodes), 0, volumeCapacity, true, "Volume"); var addressCapacity = possibleRoutes.Select(x => (long)(x.Driver.MaxRouteAddresses)).ToArray(); routing.AddDimensionWithVehicleCapacity(new CallbackAddressCount(Nodes.Length), 0, addressCapacity, true, "AddressCount"); var bottlesDimension = routing.GetDimensionOrDie("Bottles"); var addressDimension = routing.GetDimensionOrDie("AddressCount"); for (int ix = 0; ix < possibleRoutes.Length; ix++) { // Устанавливаем функцию получения стоимости маршрута. routing.SetArcCostEvaluatorOfVehicle(new CallbackDistanceDistrict(Nodes, possibleRoutes[ix], distanceCalculator), ix); // Добавляем фиксированный штраф за принадлежность водителя. if (possibleRoutes[ix].Driver.DriverType.HasValue) { routing.SetFixedCostOfVehicle(((int)possibleRoutes[ix].Driver.DriverType) * DriverPriorityPenalty, ix); } else { routing.SetFixedCostOfVehicle(DriverPriorityPenalty * 3, ix); } var cumulTimeOnEnd = routing.CumulVar(routing.End(ix), "Time"); var cumulTimeOnBegin = routing.CumulVar(routing.Start(ix), "Time"); // Устанавливаем минимальные(мягкие) границы для измерений. При значениях меньше минимальных, маршрут все таки принимается, // но вносятся некоторые штрафные очки на последнюю точку маршрута. //bottlesDimension.SetEndCumulVarSoftLowerBound(ix, possibleRoutes[ix].Car.MinBottles, MinBottlesInRoutePenalty); //addressDimension.SetEndCumulVarSoftLowerBound(ix, possibleRoutes[ix].Driver.MinRouteAddresses, MinAddressesInRoutePenalty); // Устанавливаем диапазон времени для движения по маршруту в зависимости от выбраной смены, // день, вечер и с учетом досрочного завершения водителем работы. if (possibleRoutes[ix].Shift != null) { var shift = possibleRoutes[ix].Shift; var endTime = possibleRoutes[ix].EarlyEnd.HasValue ? Math.Min(shift.EndTime.TotalSeconds, possibleRoutes[ix].EarlyEnd.Value.TotalSeconds) : shift.EndTime.TotalSeconds; cumulTimeOnEnd.SetMax((long)endTime); cumulTimeOnBegin.SetMin((long)shift.StartTime.TotalSeconds); } else if (possibleRoutes[ix].EarlyEnd.HasValue) //Устанавливаем время окончания рабочего дня у водителя. { cumulTimeOnEnd.SetMax((long)possibleRoutes[ix].EarlyEnd.Value.TotalSeconds); } } for (int ix = 0; ix < Nodes.Length; ix++) { // Проставляем на каждый адрес окно времени приезда. var startWindow = Nodes[ix].Order.DeliverySchedule.From.TotalSeconds; var endWindow = Nodes[ix].Order.DeliverySchedule.To.TotalSeconds - Nodes[ix].Order.CalculateTimeOnPoint(false); //FIXME Внимание здесь задаем время без экспедитора и без учета скорости водителя. Это не правильно, но другого варианта я придумать не смог. if (endWindow < startWindow) { AddWarning("Время разгрузки на {2}, не помещается в диапазон времени доставки. {0}-{1}", Nodes[ix].Order.DeliverySchedule.From, Nodes[ix].Order.DeliverySchedule.To, Nodes[ix].Order.DeliveryPoint.ShortAddress); endWindow = startWindow; } time_dimension.CumulVar(ix + 1).SetRange((long)startWindow, (long)endWindow); // Добавляем абсолютно все заказы в дизюкцию. Если бы заказы небыли вдобавлены в отдельные дизьюкции // то при не возможность доставить хоть один заказ. Все решение бы считаль не верным. Добавление каждого заказа // в отдельную дизьюкцию, позволяет механизму не вести какой то и заказов, и все таки формировать решение с недовезенными // заказами. Дизьюкция работает так. Он говорит, если хотя бы один заказ в этой группе(дизьюкции) доставлен, // то все хорошо, иначе штраф. Так как у нас в кадой дизьюкции по одному заказу. Мы получаем опциональную доставку каждого заказа. routing.AddDisjunction(new int[] { ix + 1 }, MaxDistanceAddressPenalty); } logger.Debug("Nodes.Length = {0}", Nodes.Length); logger.Debug("routing.Nodes() = {0}", routing.Nodes()); logger.Debug("GetNumberOfDisjunctions = {0}", routing.GetNumberOfDisjunctions()); RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters(); // Setting first solution heuristic (cheapest addition). // Указывается стратегия первоначального заполнения. Опытным путем было вычислено, что именно при // стратегиях вставки маршруты получаются с набором точек более близких к друг другу. То есть в большей // степени облачком. Что воспринималось человеком как более отпимальное. В отличии от большенства других // стратегий в которых маршруты, формируюся скорее по лентами ведущими через все обезжаемые раоны. То есть водители // чаще имели пересечения маршутов. search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion; search_parameters.TimeLimitMs = MaxTimeSeconds * 1000; // Отключаем внутреннего кеширования расчитанных значений. Опытным путем было проверено, что включение этого значения. // Значительно(на несколько секунд) увеличивает время закрытия модели и сокращает иногда не значительно время расчета оптимизаций. // И в принцепе становится целесообразно только на количествах заказов 300-400. При количестве заказов менее 200 // влючение отпечатков значений. Не уменьшало, а увеличивало общее время расчета. А при большом количестве заказов // время расчета уменьшалось не значительно. //search_parameters.FingerprintArcCostEvaluators = false; search_parameters.FingerprintArcCostEvaluators = true; //search_parameters.OptimizationStep = 100; var solver = routing.solver(); PerformanceHelper.AddTimePoint(logger, $"Настроили оптимизацию"); logger.Info("Закрываем модель..."); if ( WarningMessages.Any() && !interactiveService.Question( string.Join("\n", WarningMessages.Select(x => "⚠ " + x)), "При построении транспортной модели обнаружены следующие проблемы:\n{0}\nПродолжить?" ) ) { distanceCalculator.Canceled = true; distanceCalculator.Dispose(); return; } logger.Info("Рассчет расстояний между точками..."); routing.CloseModelWithParameters(search_parameters); #if DEBUG PrintMatrixCount(distanceCalculator.matrixcount); #endif //Записывем возможно не схраненый кеш в базу. distanceCalculator.FlushCache(); //Попытка хоть как то ослеживать что происходит в момент построения. Возможно не очень правильная. //Пришлось создавать 2 монитора. var lastSolution = solver.MakeLastSolutionCollector(); lastSolution.AddObjective(routing.CostVar()); routing.AddSearchMonitor(lastSolution); routing.AddSearchMonitor(new CallbackMonitor(solver, StatisticsTxtAction, lastSolution)); PerformanceHelper.AddTimePoint(logger, $"Закрыли модель"); logger.Info("Поиск решения..."); Assignment solution = routing.SolveWithParameters(search_parameters); PerformanceHelper.AddTimePoint(logger, $"Получили решение."); logger.Info("Готово. Заполняем."); #if DEBUG PrintMatrixCount(distanceCalculator.matrixcount); #endif Console.WriteLine("Status = {0}", routing.Status()); if (solution != null) { // Solution cost. Console.WriteLine("Cost = {0}", solution.ObjectiveValue()); time_dimension = routing.GetDimensionOrDie("Time"); //Читаем полученные маршруты. for (int route_number = 0; route_number < routing.Vehicles(); route_number++) { var route = new ProposedRoute(possibleRoutes[route_number]); long first_node = routing.Start(route_number); long second_node = solution.Value(routing.NextVar(first_node)); // Пропускаем первый узел, так как это наша база. route.RouteCost = routing.GetCost(first_node, second_node, route_number); while (!routing.IsEnd(second_node)) { var time_var = time_dimension.CumulVar(second_node); var rPoint = new ProposedRoutePoint( TimeSpan.FromSeconds(solution.Min(time_var)), TimeSpan.FromSeconds(solution.Max(time_var)), Nodes[second_node - 1].Order ); rPoint.DebugMaxMin = string.Format("\n({0},{1})[{3}-{4}]-{2} Cost:{5}", new DateTime().AddSeconds(solution.Min(time_var)).ToShortTimeString(), new DateTime().AddSeconds(solution.Max(time_var)).ToShortTimeString(), second_node, rPoint.Order.DeliverySchedule.From.ToString("hh\\:mm"), rPoint.Order.DeliverySchedule.To.ToString("hh\\:mm"), routing.GetCost(first_node, second_node, route_number) ); route.Orders.Add(rPoint); first_node = second_node; second_node = solution.Value(routing.NextVar(first_node)); route.RouteCost += routing.GetCost(first_node, second_node, route_number); } if (route.Orders.Count > 0) { ProposedRoutes.Add(route); logger.Debug("Маршрут {0}: {1}", route.Trip.Driver.ShortName, string.Join(" -> ", route.Orders.Select(x => x.DebugMaxMin)) ); } else { logger.Debug("Маршрут {0}: пустой", route.Trip.Driver.ShortName); } } } #if DEBUG logger.Debug("SGoToBase:{0}", string.Join(", ", CallbackDistanceDistrict.SGoToBase.Select(x => $"{x.Key.Driver.ShortName}={x.Value}"))); logger.Debug("SFromExistPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SFromExistPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}"))); logger.Debug("SUnlikeDistrictPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SUnlikeDistrictPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}"))); logger.Debug("SLargusPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SLargusPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}"))); #endif if (ProposedRoutes.Count > 0) { logger.Info($"Предложено {ProposedRoutes.Count} маршрутов."); } PerformanceHelper.Main.PrintAllPoints(logger); if (distanceCalculator.ErrorWays.Any()) { logger.Debug("Ошибок получения расстояний {0}", distanceCalculator.ErrorWays.Count); var uniqueFrom = distanceCalculator.ErrorWays.Select(x => x.FromHash).Distinct().ToList(); var uniqueTo = distanceCalculator.ErrorWays.Select(x => x.ToHash).Distinct().ToList(); logger.Debug("Уникальных точек: отправки = {0}, прибытия = {1}", uniqueFrom.Count, uniqueTo.Count); logger.Debug("Проблемные точки отправки:\n{0}", string.Join("; ", distanceCalculator.ErrorWays .GroupBy(x => x.FromHash) .Where(x => x.Count() > (uniqueTo.Count / 2)) .Select(x => CachedDistance.GetText(x.Key))) ); logger.Debug("Проблемные точки прибытия:\n{0}", string.Join("; ", distanceCalculator.ErrorWays .GroupBy(x => x.ToHash) .Where(x => x.Count() > (uniqueFrom.Count / 2)) .Select(x => CachedDistance.GetText(x.Key))) ); } }
private static void VRP(int[,] locations, int[] demands, int num_vehicles) { int num_locations = locations.GetLength(0); int depot = 0; // The depot is the start and end point of each route. // Create routing model if (locations.Length > 0) { RoutingModel routing = new RoutingModel(num_locations, num_vehicles, depot); var search_parameters = RoutingModel.DefaultSearchParameters(); // Setting first solution heuristic: the // method for finding a first solution to the problem. search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; var distanceCallback = new DistanceLocationsCallback(locations); // Display(distanceCallback.matrix); routing.SetArcCostEvaluatorOfAllVehicles(distanceCallback); // Add a dimension for demand. long slack_max = 0; long vehicle_capacity = 100; bool fix_start_cumul_to_zero = true; string demand = "Demand"; routing.AddDimension(new DemandCallback(demands), slack_max, vehicle_capacity, fix_start_cumul_to_zero, demand); // Solve, displays a solution if any. var assignment = routing.SolveWithParameters(search_parameters); Console.WriteLine("Status = {0}", routing.Status()); if (assignment != null) { // Solution cost. Console.WriteLine("Total distance of all routes: {0}", assignment.ObjectiveValue()); // Inspect solution. // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 for (int vehicle_nbr = 0; vehicle_nbr < num_vehicles; vehicle_nbr++) { long index = routing.Start(vehicle_nbr); long index_next = assignment.Value(routing.NextVar(index)); string route = string.Empty; long route_dist = 0; long route_demand = 0; int node_index; int node_index_next; while (!routing.IsEnd(index_next)) { // Convert variable indices to node indices in the displayed route. node_index = routing.IndexToNode(index); node_index_next = routing.IndexToNode(index_next); route += node_index + " -> "; // Add the distance to the next node. route_dist += distanceCallback.Run(node_index, node_index_next); // # Add demand. route_demand += demands[node_index_next]; index = index_next; index_next = assignment.Value(routing.NextVar(index)); } node_index = routing.IndexToNode(index); node_index_next = routing.IndexToNode(index_next); route += node_index + " -> " + node_index_next; route_dist += distanceCallback.Run(node_index, node_index_next); Console.WriteLine("Route for vehicle " + vehicle_nbr + ":\n\n" + route + "\n"); Console.WriteLine("Distance of route " + vehicle_nbr + ": " + route_dist); Console.WriteLine("Demand met by vehicle " + vehicle_nbr + ": " + route_demand + "\n"); // Console.WriteLine($"Route: {route}"); } } else { Console.WriteLine("No solution found."); } } }
/// <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)); }
public static void Solve(int vehicles, Distance distances) { /* * Generate constraint model */ // Third argument defines depot, i.e. start-end node for round trip. var model = new RoutingModel(distances.MapSize(), vehicles, 0); // Node costs vs. Arc Costs model.SetArcCostEvaluatorOfAllVehicles(distances); /* * A vehicle must not visit more than 7 cities */ //model.AddConstantDimension(1, 7, true, "count"); /* * A vehicle must visit at least 3 cities */ /*model.AddConstantDimension(1, Int32.MaxValue, true, "count"); * var count = model.GetDimensionOrDie("count"); * for (int i = 0; i < vehicles; i++) { * count.CumulVar(model.End(i)).SetMin(3); * }*/ /* * City 3 and 5 must NOT be visited by the same vehicle */ //model.solver().Add(model.VehicleVar(3) != model.VehicleVar(5)); /* * City 3 must be visited before city 5 (not necessarily by the same vehicle) */ /*model.AddConstantDimension(1, Int32.MaxValue, true, "time"); * var time = model.GetDimensionOrDie("time"); * model.solver().Add(time.CumulVar(3) < time.CumulVar(5));*/ /* * City 3 must be visited right after city 5 (not necessarily by the same vhicle) */ /*model.AddConstantDimension(1, Int32.MaxValue, true, "time"); * var time = model.GetDimensionOrDie("time"); * model.solver().Add(time.CumulVar(5) + 1 == time.CumulVar(3));*/ /* * Solve problem and display solution */ Assignment assignment = model.Solve(); if (assignment != null) { Console.WriteLine("Depot: " + model.GetDepot()); Console.WriteLine("Total Distance: " + assignment.ObjectiveValue() + "\n"); for (int i = 0; i < vehicles; i++) { /* * Display Trips: */ Console.WriteLine("Round Trip for Vehicle " + i + ":"); long total = 0; var source = model.Start(i); while (!model.IsEnd(source)) { var target = model.Next(assignment, source); var from = model.IndexToNode(source); var to = model.IndexToNode(target); total += distances.Run(from, to); Console.WriteLine( $" - From {distances.ToString(@from),-3} travel to {distances.ToString(to),-3} with distance: {distances.Run(@from, to),-3}"); source = target; } Console.WriteLine("Total Distance for Vehicle " + i + ": " + total + "\n"); } } Console.ReadKey(); }
/// <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; RoutingIndexManager manager = new RoutingIndexManager(number_of_locations, number_of_vehicles, vehicle_starts_, vehicle_ends_); RoutingModel model = new RoutingModel(manager); // Setting up dimensions const int big_number = 100000; Manhattan manhattan_callback = new Manhattan(manager, locations_, 1); model.AddDimension(model.RegisterTransitCallback(manhattan_callback.Call), big_number, big_number, false, "time"); RoutingDimension time_dimension = model.GetDimensionOrDie("time"); Demand demand_callback = new Demand(manager, order_demands_); model.AddDimension(model.RegisterUnaryTransitCallback(demand_callback.Call), 0, vehicle_capacity_, true, "capacity"); RoutingDimension capacity_dimension = model.GetDimensionOrDie("capacity"); // Setting up vehicles Manhattan[] cost_callbacks = new Manhattan[number_of_vehicles]; for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { int cost_coefficient = vehicle_cost_coefficients_[vehicle]; Manhattan manhattan_cost_callback = new Manhattan(manager, locations_, cost_coefficient); cost_callbacks[vehicle] = manhattan_cost_callback; int manhattan_cost_index = model.RegisterTransitCallback(manhattan_cost_callback.Call); model.SetArcCostEvaluatorOfVehicle(manhattan_cost_index, vehicle); time_dimension.CumulVar(model.End(vehicle)).SetMax(vehicle_end_time_[vehicle]); } // Setting up orders for (int order = 0; order < number_of_orders; ++order) { time_dimension.CumulVar(order).SetRange(order_time_windows_[order].start_, order_time_windows_[order].end_); long[] orders = { manager.NodeToIndex(order) }; model.AddDisjunction(orders, order_penalties_[order]); } // Solving RoutingSearchParameters search_parameters = operations_research_constraint_solver.DefaultRoutingSearchParameters(); search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.AllUnperformed; Console.WriteLine("Search..."); Assignment solution = model.SolveWithParameters(search_parameters); 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 = capacity_dimension.CumulVar(order); IntVar local_time = time_dimension.CumulVar(order); route += order + " Load(" + solution.Value(local_load) + ") " + "Time(" + solution.Min(local_time) + ", " + solution.Max(local_time) + ") -> "; } IntVar load = capacity_dimension.CumulVar(order); IntVar time = time_dimension.CumulVar(order); route += order + " Load(" + solution.Value(load) + ") " + "Time(" + solution.Min(time) + ", " + solution.Max(time) + ")"; } output += route + "\n"; } Console.WriteLine(output); } }
public void Init() { if (DataModel != null) { // Create RoutingModel Index RoutingIndexManager if (DataModel.Starts != null && DataModel.Ends != null) { RoutingIndexManager = new RoutingIndexManager( DataModel.TravelTimes.GetLength(0), DataModel.VehicleCapacities.Length, DataModel.Starts, DataModel.Ends); } else { throw new Exception("Starts or Ends in DataModel is null"); } //Create routing model RoutingModel = new RoutingModel(RoutingIndexManager); // Create and register a transit callback. var transitCallbackIndex = RoutingModel.RegisterTransitCallback( (long fromIndex, long toIndex) => { // Convert from routing variable Index to time matrix or distance matrix NodeIndex. var fromNode = RoutingIndexManager.IndexToNode(fromIndex); var toNode = RoutingIndexManager.IndexToNode(toIndex); return(DataModel.TravelTimes[fromNode, toNode]); } ); //Create and register demand callback var demandCallbackIndex = RoutingModel.RegisterUnaryTransitCallback( (long fromIndex) => { // Convert from routing variable Index to demand NodeIndex. var fromNode = RoutingIndexManager.IndexToNode(fromIndex); return(DataModel.Demands[fromNode]); } ); if (DropNodesAllowed) { // Allow to drop nodes. //The penalty should be larger than the sum of all travel times locations (excluding the depot). //As a result, after dropping one location to make the problem feasible, the solver won't drop any additional locations, //because the penalty for doing so would exceed any further reduction in travel time. //If we want to make as many deliveries as possible, penalty value should be larger than the sum of all travel times between locations long penalty = 99999999; for (int j = 0; j < DataModel.Starts.GetLength(0); j++) { var startIndex = DataModel.Starts[j]; for (int i = 0; i < DataModel.TravelTimes.GetLength(0); ++i) { if (startIndex != i) { RoutingModel.AddDisjunction(new long[] { RoutingIndexManager.NodeToIndex(i) }, penalty);//adds disjunction to all stop besides start stops } } } } var vehicleCost = 10000; RoutingModel.SetFixedCostOfAllVehicles(vehicleCost); //adds a penalty for using each vehicle RoutingModel.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); //Sets the cost function of the model such that the cost of a segment of a route between node 'from' and 'to' is evaluator(from, to), whatever the route or vehicle performing the route. //Adds capacity constraints RoutingModel.AddDimensionWithVehicleCapacity( demandCallbackIndex, 0, // null capacity slack DataModel.VehicleCapacities, // vehicle maximum capacities false, // start cumul to zero "Capacity"); RoutingDimension capacityDimension = RoutingModel.GetMutableDimension("Capacity"); //Add Time window constraints RoutingModel.AddDimension( transitCallbackIndex, // transit callback 86400, // allow waiting time (24 hours in seconds) 86400, // maximum travel time per vehicle (24 hours in seconds) DataModel.ForceCumulToZero, // start cumul to zero "Time"); RoutingDimension timeDimension = RoutingModel.GetMutableDimension("Time"); //timeDimension.SetGlobalSpanCostCoefficient(10); var solver = RoutingModel.solver(); // Add time window constraints for each location except depot. for (int i = 0; i < DataModel.TimeWindows.GetLength(0); i++) { long index = RoutingIndexManager.NodeToIndex(i); //gets the node index if (index != -1) { var lowerBound = DataModel.TimeWindows[i, 0]; //minimum time to be at current index (lower bound for the timeWindow of current Index) var softUpperBound = DataModel.TimeWindows[i, 1]; //soft maxUpperBound for the timeWindow at current index var upperBound = softUpperBound + MaximumDeliveryDelayTime; //maxUpperBound to be at current index (upperbound for the timeWindow at current index) //softupperbound and upperbound are different because the upperbound is usually bigger than the softuppberbound in order to soften the current timeWindows, enabling to generate a solution that accomodates more requests timeDimension.CumulVar(index).SetRange(lowerBound, upperBound); //sets the maximum upper bound and lower bound limit for the timeWindow at the current index timeDimension.SetCumulVarSoftUpperBound(index, softUpperBound, 10000); //adds soft upper bound limit which is the requested time window RoutingModel.AddToAssignment(timeDimension.SlackVar(index)); //add timeDimension slack var for current index to the assignment RoutingModel.AddToAssignment(timeDimension.TransitVar(index)); // add timeDimension transit var for current index to the assignment RoutingModel.AddToAssignment(capacityDimension.TransitVar(index)); //add transit capacity var for current index to assignment } } // Add time window constraints for each vehicle start node, and add to assignment the slack and transit vars for both dimensions for (int i = 0; i < DataModel.VehicleCapacities.Length; i++) { long index = RoutingModel.Start(i); var startDepotIndex = DataModel.Starts[i]; timeDimension.CumulVar(index).SetRange(DataModel.TimeWindows[startDepotIndex, 0], DataModel.TimeWindows[startDepotIndex, 1]); //this guarantees that a vehicle must visit the location during its time RoutingModel.AddToAssignment(timeDimension.SlackVar(index)); //add timeDimension slack var for depot index for vehicle i depotto assignment RoutingModel.AddToAssignment(timeDimension.TransitVar(index)); //add timeDimension transit var for depot index for vehicle i depot to assignment RoutingModel.AddToAssignment(capacityDimension.TransitVar(index)); //add capacityDimension transit var for vehicle i depot } //Add client max ride time constraint, enabling better service quality for (int i = 0; i < DataModel.PickupsDeliveries.Length; i++) //iterates over each pickupDelivery pair { int vehicleIndex = -1; if (DataModel.PickupsDeliveries[i][0] == -1) //if the pickupDelivery is a customer inside a vehicle { vehicleIndex = DataModel.CustomersVehicle[i]; //gets the vehicle index } var pickupIndex = vehicleIndex == -1 ? RoutingIndexManager.NodeToIndex(DataModel.PickupsDeliveries[i][0]):RoutingModel.Start(vehicleIndex); //if is a customer inside a vehicle the pickupIndex will be the vehicle startIndex, otherwise its the customers real pickupIndex var deliveryIndex = RoutingIndexManager.NodeToIndex(DataModel.PickupsDeliveries[i][1]); var rideTime = DataModel.CustomersRideTimes[i]; var directRideTimeDuration = DataModel.TravelTimes[pickupIndex, DataModel.PickupsDeliveries[i][1]]; var realRideTimeDuration = rideTime + (timeDimension.CumulVar(deliveryIndex) - timeDimension.CumulVar(pickupIndex)); //adds the currentRideTime of the customer and subtracts cumulative value of the ride time of the delivery index with the current one of the current index to get the real ride time duration solver.Add(realRideTimeDuration < directRideTimeDuration + DataModel.MaxCustomerRideTime); //adds the constraint so that the current ride time duration does not exceed the directRideTimeDuration + maxCustomerRideTimeDuration } //Add precedence and same vehicle Constraints for (int i = 0; i < DataModel.PickupsDeliveries.GetLength(0); i++) { if (DataModel.PickupsDeliveries[i][0] != -1) { long pickupIndex = RoutingIndexManager.NodeToIndex(DataModel.PickupsDeliveries[i][0]); //pickup index long deliveryIndex = RoutingIndexManager.NodeToIndex(DataModel.PickupsDeliveries[i][1]); //delivery index RoutingModel.AddPickupAndDelivery(pickupIndex, deliveryIndex); //Notifies that the pickupIndex and deliveryIndex form a pair of nodes which should belong to the same route. solver.Add(solver.MakeEquality(RoutingModel.VehicleVar(pickupIndex), RoutingModel.VehicleVar(deliveryIndex))); //Adds a constraint to the solver, that defines that both these pickup and delivery pairs must be picked up and delivered by the same vehicle (same route) solver.Add(solver.MakeLessOrEqual(timeDimension.CumulVar(pickupIndex), timeDimension.CumulVar(deliveryIndex))); //Adds the precedence constraint to the solver, which defines that each item must be picked up at pickup index before it is delivered to the delivery index //timeDimension.SlackVar(pickupIndex).SetMin(4);//mininimum slack will be 3 seconds (customer enter timer) //timeDimension.SlackVar(deliveryIndex).SetMin(3); //minimum slack will be 3 seconds (customer leave time) } } //Constraints to enforce that if there is a customer inside a vehicle, it has to be served by that vehicle for (int customerIndex = 0; customerIndex < DataModel.CustomersVehicle.GetLength(0); customerIndex++) { var vehicleIndex = DataModel.CustomersVehicle[customerIndex]; if (vehicleIndex != -1) //if the current customer is inside a vehicle { var vehicleStartIndex = RoutingModel.Start(vehicleIndex); //vehicle start depot index var deliveryIndex = RoutingIndexManager.NodeToIndex(DataModel.PickupsDeliveries[customerIndex][1]); //gets the deliveryIndex solver.Add(solver.MakeEquality(RoutingModel.VehicleVar(vehicleStartIndex), RoutingModel.VehicleVar(deliveryIndex))); //vehicle with vehicleIndex has to be the one that delivers customer with nodeDeliveryIndex; //this constraint enforces that the vehicle indexed by vehicleIndex has to be the vehicle which services (goes to) the nodeDeliveryIndex as well } } for (int i = 0; i < DataModel.VehicleCapacities.Length; i++) { RoutingModel.AddVariableMinimizedByFinalizer( timeDimension.CumulVar(RoutingModel.Start(i))); RoutingModel.AddVariableMinimizedByFinalizer( timeDimension.CumulVar(RoutingModel.End(i))); } } }
static void Main(string[] args) { List <string> Addresses = new List <string>() { "New York", "Los Angeles", "Chicago", "Minneapolis", "Denver", "Dallas", "Seattle", "Boston", "San Francisco", "St.Louis", "Houston", "Phoenix", "Salt Lake City" }; APIGoogle.Register("AIzaSyCkYDMEPLWCFvq3Oi-LJyEsMuh_06Fk62g"); List <LatLng> listLatLng = new List <LatLng>(); foreach (string item in Addresses) { listLatLng.Add(APIGoogle.GetLatLng(item)); } DirectionRequest directionRequest = new DirectionRequest(); DirectionService directionService = new DirectionService(); long[,] CityDistanceMatrix = new long[Addresses.Count, Addresses.Count]; for (int i = 0; i < Addresses.Count; i++) { for (int j = 0; j < Addresses.Count; j++) { directionRequest.Origin = Addresses[i]; directionRequest.Sensor = false; { directionRequest.Destination = Addresses[j]; var ttt = directionService.GetResponse(directionRequest); CityDistanceMatrix[i, j] = directionService.GetResponse(directionRequest).Routes[0].Legs[0].Distance.Value / 1000; }; } } int NumRoutes = 1; // The number of routes, which is 1 in the TSP. // Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route. int Depot = 0; int TspSize = Addresses.Count; if (TspSize > 0) { RoutingModel routing = new RoutingModel(TspSize, NumRoutes, Depot); RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters(); CityDistance dist_between_nodes = new CityDistance(CityDistanceMatrix, Addresses); routing.SetArcCostEvaluatorOfAllVehicles(dist_between_nodes); //routing.SetCost(dist_between_nodes); Demand demands_at_locations = new Demand(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }); search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; Assignment solution = routing.SolveWithParameters(search_parameters); Console.WriteLine("Status = {0}", routing.Status()); if (solution != null) { // Solution cost. Console.WriteLine("Suma [km]= {0}", solution.ObjectiveValue() / 1000); // Inspect solution. // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 int route_number = 0; for (long node = routing.Start(route_number); !routing.IsEnd(node); node = solution.Value(routing.NextVar(node))) { Console.Write("{0} \n", Addresses[(int)node]); } Console.WriteLine(Addresses[0]); } } Console.ReadKey(); }
/// <summary> /// Solves the current routing problem. /// </summary> static void Solve() { // Instantiate the data problem. // [START data] const int num_location = 5; const int num_vehicles = 1; const int depot = 0; // [END data] // Create Routing Index Manager // [START index_manager] RoutingIndexManager manager = new RoutingIndexManager(num_location, num_vehicles, depot); // [END index_manager] // Create Routing Model. // [START routing_model] RoutingModel routing = new RoutingModel(manager); // [END routing_model] // Define cost of each arc. // [START arc_cost] routing.SetArcCostEvaluatorOfAllVehicles( routing.RegisterTransitCallback( (long FromIndex, long ToIndex) => { return(1L); } )); // [END arc_cost] // 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] Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); // Inspect solution. long index = routing.Start(0); Console.WriteLine("Route for Vehicle 0:"); long route_distance = 0; while (routing.IsEnd(index) == false) { Console.Write("{0} -> ", manager.IndexToNode((int)index)); long previousIndex = index; index = solution.Value(routing.NextVar(index)); route_distance += routing.GetArcCostForVehicle(previousIndex, index, 0); } Console.WriteLine("{0}", manager.IndexToNode(index)); Console.WriteLine("Distance of the route: {0}m", route_distance); // [END print_solution] }
/* * Number of Vehicles: */ public static void Solve(int vehicles, string pathToXml = null) { /* * Add custom distance function */ var dist = (pathToXml == null) ? new Distance() : new XmlDistance(pathToXml); /* * Generate constraint model */ // Third argument defines depot, i.e. start-end node for round trip. var model = new RoutingModel(dist.MapSize(), vehicles, 0); model.SetCost(dist); /* * This modification forces all Vehicles to visit at least one city. */ /*for (int i = 0; i < Vehicles; i++) { * * IntVar first = model.NextVar(model.Start(i)); * first.SetMax(dist.MapSize() - 1); * } * * /* * Solve problem and display solution */ Assignment assignment = model.Solve(); if (assignment != null) { Console.WriteLine("Total Distance: " + assignment.ObjectiveValue() + "\n"); for (int i = 0; i < vehicles; i++) { /* * Display Round Trip: */ Console.WriteLine("Round Trip for Vehicle " + i + "\n"); for (long node = model.Start(i); node < model.End(i); node = model.Next(assignment, node)) { Console.Write(node + " -> "); } Console.WriteLine(model.Start(i) + "\n"); /* * Display individual Section Distances for Verification: */ var source = (int)model.Start(i); while (source < model.End(i)) { var target = (int)model.Next(assignment, source); if (source < dist.MapSize() && target < dist.MapSize()) { Console.WriteLine("From " + source + " travel to " + target + " -> distance = " + dist.Run(source, target)); } else if (source < dist.MapSize()) { Console.WriteLine("From " + source + " travel to 0 -> distance = " + dist.Run(source, 0)); } source = target; } Console.WriteLine("\n"); } } Console.ReadKey(); }