google / or-tools

Google's Operations Research tools:
https://developers.google.com/optimization/
Apache License 2.0
11.27k stars 2.13k forks source link

Really bad results on a simple CVRPTW #2006

Closed dimboukosis closed 4 years ago

dimboukosis commented 4 years ago

using: C#, Routing Hello everyone, I am currently trying to solve a simple CVRPTW. I can post my code if needed.

I have created a dummy example, which is tested to be feasible including 7 vehicles (capacity varies from 1000 to 3000 kg) and 10 nodes (demand varies from 6 to 117 kg). Each node has an average service time of 10 minutes and the transit between each node takes 5 minutes, for now, for the sake of simplicity. Distances are calculated using the haversine distance between each point on the map (as I am using coordinates). Vehicles' starting and ending times are 07:00-17:00 and orders' time window start and end are 07:00-18:00. Also I have added disjunctions for each node if dropped. Hence, a solution exists with absolute certainty (all nodes dropped).

I have created a solution myself using only one vehicle (the big one) and routed all nodes with a total distance of 77km and capacity used 525kg out of the 3000kg. Starting at 07:00 and ending at 09:45.

The solver creates a solution that uses all 7 of the vehicles (1-3 nodes each), some of them seriously underutilized (loading only 6kg in the biggest vehicle) and with total traveled distance at 136km (2 times worst than mine). When I run the problem with only one vehicle the solver returns my solution.

And besides that, only one first solution strategy can even run the problem (pathCheapestArc). All other first solution strategies don't even run (if they run they would return a solution with all nodes dropped). I have tested the dataset with tabu search for a time up to 10 minutes, but the solution remains the same.

Is there anything wrong in my way of thinking? Kind Regards, D

dimboukosis commented 4 years ago

This is my code.

InputReader reader = new InputReader();
DataModel data = reader.ReadFile();

RoutingIndexManager manager = new RoutingIndexManager(
                data.DistanceMatrix.GetLength(0), //Number of Nodes
                VehicleNumber,                    //Number of Vehicles
                data.Depot);                      //Index of 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);

int timeCallbackIndex = routing.RegisterTransitCallback(
                (long fromIndex, long toIndex) =>
                {
                    var fromNode = manager.IndexToNode(fromIndex);
                    var toNode = manager.IndexToNode(toIndex);
                    return (5);
                }
            );

routing.AddDimension(
                timeCallbackIndex, //which callback it calls
                0,                 //Max slack at each node
                1439,              //Total capacity accumulated along each route
                false,             //Start cumulative to zero time
                "Time");
            RoutingDimension timeDimension = routing.GetDimensionOrDie("Time");

//Add time window for each order
            for (int i = 1; i < data.Orders.Count; ++i)
            {
                long index = manager.NodeToIndex(i);
                timeDimension.CumulVar(index).
                    SetRange
                    (data.Orders[i].OpeningTimeWindow,data.Orders[i].ClosingTimeWindow);
            }

 for (int i = 0; i < VehicleNumber; ++i)
  {
                //Each vehicle is available from vehicle start to vehicle end
                long index = routing.Start(i);
                timeDimension.CumulVar(index).SetRange(data.Vehicles[i].VehicleStart, data.Vehicles[i].VehicleEnd);    
  }

int demandCallbackIndex = routing.RegisterUnaryTransitCallback(
(long fromIndex) =>
{
     // Convert from routing variable Index to demand NodeIndex.
     var fromNode = manager.IndexToNode(fromIndex);
     return Demands[fromNode];
}
);

routing.AddDimensionWithVehicleCapacity(
                demandCallbackIndex, 
                0,                         // null capacity slack
                VehicleCapacities,         // vehicle maximum capacities
                true,                      // start cumul to zero
                "Capacity");

long penalty = 100000000;
            for (int i = 1; i < data.DistanceMatrix.GetLength(0); ++i)
            {
                routing.AddDisjunction(new long[] { manager.NodeToIndex(i) }, penalty);
            }

RoutingSearchParameters searchParameters = operations_research_constraint_solver.DefaultRoutingSearchParameters();
            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; 
            searchParameters.LogSearch = true;
dimboukosis commented 4 years ago

In addition, after making some tests, I figured that the moment I erased the time dimension and time callback, everything ran smoothly and the solution returned was probably the optimal.

Mizux commented 4 years ago

My two cents:

Please prefer to use the mailing list for "modeling optimization"