Ejemplo n.º 1
0
        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)));
                }
            }
        }