コード例 #1
0
        public void SimpleLambdaCallback(bool callGC)
        {
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(
                5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/);
            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);
            // Create a distance callback.
            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(Math.Abs(toNode - fromNode));
            });

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
            if (callGC)
            {
                GC.Collect();
            }
            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.PathCheapestArc;
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+4)-> 0 := +8
            Assert.Equal(8, solution.ObjectiveValue());
        }
コード例 #2
0
ファイル: RoutingSolverTests.cs プロジェクト: weexp/or-tools
        public void TestVectorDimension()
        {
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/);
            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            // Create a distance callback.
            long[]      vector = new long[] { 1, 1, 1, 1, 1 };
            IntBoolPair result = routing.AddVectorDimension(
                vector,
                /*capacity=*/ 10,
                /*fix_start_cumul_to_zero=*/ true,
                "Dimension");

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(result.first);
            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5
            Assert.Equal(5, solution.ObjectiveValue());
        }
コード例 #3
0
ファイル: RoutingSolverTests.cs プロジェクト: weexp/or-tools
        public void TestTransitMatrix()
        {
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/);
            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            // Create a distance callback.
            long[][] matrix = new long[][] {
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
            };
            int transitCallbackIndex = routing.RegisterTransitMatrix(matrix);

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5
            Assert.Equal(5, solution.ObjectiveValue());
        }
コード例 #4
0
        public Assignment TryGetSolution(RoutingSearchParameters searchParameters)
        {
            Assignment solution = null;

            if (searchParameters == null)
            {
                searchParameters = GetDefaultSearchParameters();
            }
            //for loop that tries to find the earliest feasible solution (trying to minimize the maximum upper bound) within a maximum delay delivery time (upper bound), using the current customer requests
            for (int currentMaximumDelayTime = 0; currentMaximumDelayTime < DataModel.MaxAllowedDeliveryDelayTime; currentMaximumDelayTime = currentMaximumDelayTime + 60) //iterates adding 1 minute to maximum allowed timeWindow (60 seconds) if a feasible solution isnt found for the current upperbound
            {
                MaximumDeliveryDelayTime = currentMaximumDelayTime;
                Init();
                //Get the solution of the problem
                try
                {
                    solution = RoutingModel.SolveWithParameters(searchParameters);
                }
                catch (Exception)
                {
                    solution = null;
                }

                if (solution != null) //if true, solution was found, breaks the cycle
                {
                    break;
                }
            }

            Console.WriteLine("Solver status:" + GetSolverStatus());
            return(solution); //retuns null if no solution is found, otherwise returns the solution
        }
コード例 #5
0
        public static int[] Run(long[] DistanceMatrix, int num, int VehicleNumber, int Depot, RoutingSearchParameters searchParameters)
        {
            // Instantiate the data problem.
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(
                num,
                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(DistanceMatrix[fromNode * num + toNode]);
            }
                );

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

            // Solve the problem.
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // Print solution on console.
            return(GetOrder(routing, manager, solution));
        }
コード例 #6
0
    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");
        }
    }
コード例 #7
0
ファイル: Program.cs プロジェクト: arwa-techpk/GoogleORTools
        /// <summary>
        ///   Print the solution.
        /// </summary>
        static void VRP_LongestSingleRoute()
        {
            Console.WriteLine("VRP_LongestSingleRoute!");
            // Instantiate the data problem.
            ORDataModel data = new ORDataModel();

            // public RoutingIndexManager(int num_nodes, int num_vehicles, int depot);
            //public RoutingIndexManager(int num_nodes, int num_vehicles, int[] starts, int[] ends);

            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(
                data.DistanceMatrix.GetLength(0),
                data.VehicleNumber,
                data.Depot);


            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            // Create and register a transit callback.
            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]);
            }
                );

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

            // Add Distance constraint.
            routing.AddDimension(transitCallbackIndex, 0, 3000,
                                 true, // start cumul to zero
                                 "Distance");
            RoutingDimension distanceDimension = routing.GetMutableDimension("Distance");

            distanceDimension.SetGlobalSpanCostCoefficient(100);

            // 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);

            // Print solution on console.
            PrintSolutionPathCheapest(data, routing, manager, solution);
        }
コード例 #8
0
        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;
        }
コード例 #9
0
        public FileOutput Solve(FileInput input, RoutingSearchParameters searchParameters)
        {
            WorkData data = converter.ToWorkData(input);

            RoutingIndexManager manager = new RoutingIndexManager(data.DistanceMatrix.GetLength(0), data.VehiclesAmount, data.Starts, data.Ends);
            RoutingModel        routing = new RoutingModel(manager);

            AddCapacityConstrains(data, manager, routing);
            AddTimeWindowConstrains(data, manager, routing);
            AddPenaltiesAndDroppingVisits(data, manager, routing);

            Assignment solution = routing.SolveWithParameters(searchParameters);

            if (solution == null)
            {
                return(null);
            }
            return(converter.ConvertToFileOutput(input, manager, routing, solution));
        }
コード例 #10
0
        /// <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);
        }
コード例 #11
0
        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());
        }
コード例 #12
0
ファイル: TspService.cs プロジェクト: bkrastev95/MyGuide
        public List <Destination> SolveTsp(List <Destination> destinations)
        {
            var tspSize = destinations.Count;
            // One route because there is one traveller:
            var numberOfRoutes = 1;
            // The homet town is inserted at position 0 in the list, so the "depot" index is 0:
            var depotIndex = 0;

            // Compose the model:
            var model = new RoutingModel(tspSize, numberOfRoutes, depotIndex);

            // Setup search params
            var searchParameters = RoutingModel.DefaultSearchParameters();

            // Find a first solution with the cheapest addition algorithm:
            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
            // Use GLS for local search metaheuristic:
            searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.GuidedLocalSearch;
            // Lower valuest make the local search more fine-grained
            searchParameters.GuidedLocalSearchLambdaCoefficient = 0.1;
            // The calculations are given one second to perform:
            searchParameters.TimeLimitMs = 1000;

            //Setup arc costs evaluation:
            var arcCostEvaluator = new DistanceCallback(destinations.Select(d => new Point {
                X = d.X, Y = d.Y
            }).ToList());

            model.SetArcCostEvaluatorOfAllVehicles(arcCostEvaluator);

            // Solve the problem:
            var solution = model.SolveWithParameters(searchParameters);

            if (solution != null)
            {
                return(ExtractWaybillsFromSolution(solution, model, destinations));
            }

            return(new List <Destination>());
        }
コード例 #13
0
ファイル: cscvrptw.cs プロジェクト: yugInd8/or-tools
    /// <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);
        }
    }
コード例 #14
0
        //IntVar x;
        //IntVar y;//Reduntant

        public void SolveVrpProblem(Day day, ConfigParams cfg, VrpProblem vrpProblem, IDataOutput dataOutput, int[] VCMinMax)
        {
            this.day = day;
            this.cfg = cfg;
            //Google Distance Matrix API (Duration matrix)


            // Create Routing Index Manager
            manager = new RoutingIndexManager(
                day.TimeMatrix.GetLength(0),
                day.Vehicles.Count,
                day.Depot);


            // Create Routing Model.
            routing = new RoutingModel(manager);

            // Create and register a transit callback.
            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(day.TimeMatrix[fromNode, toNode]);
            }
                );

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

            // Add Distance constraint.

            if (day.TimeWindowsActive != true)
            {
                routing.AddDimension(transitCallbackIndex, 0, 700000,
                                     true, // start cumul to zero
                                     "Distance");
                RoutingDimension distanceDimension = routing.GetMutableDimension("Distance");
                distanceDimension.SetGlobalSpanCostCoefficient(100);
            }
            else
            {
                routing.AddDimension(
                    transitCallbackIndex, // transit callback
                    1000,                 // allow waiting time
                    600,                  // vehicle maximum capacities
                    false,                // start cumul to zero
                    "Time");

                TimeWindowInit(day, routing, manager);//Set Time Window Constraints
            }
            if (day.MaxVisitsActive != 0)
            {
                int demandCallbackIndex = routing.RegisterUnaryTransitCallback(
                    (long fromIndex) => {
                    // Convert from routing variable Index to demand NodeIndex.
                    var fromNode = manager.IndexToNode(fromIndex);
                    return(day.Demands[fromNode]);
                }
                    );

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

            // Allow to drop nodes.
            for (int i = 1; i < day.TimeMatrix.GetLength(0); ++i)
            {
                routing.AddDisjunction(
                    new long[] { manager.NodeToIndex(i) }, day.Locations.ElementAt(i).Penalty);
            }

            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();


            searchParameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.PathMostConstrainedArc;

            //metaheuristic
            searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.GuidedLocalSearch;
            searchParameters.TimeLimit = new Duration {
                Seconds = cfg.SolutionDuration
            };
            searchParameters.LogSearch = true;

            solver = routing.solver();

            //TODO
            //Some location must be on same route.
            //solver.Add(routing.VehicleVar(manager.NodeToIndex(2)) == routing.VehicleVar(manager.NodeToIndex(5)));
            //One node takes precedence over the another.
            //routing.AddPickupAndDelivery(manager.NodeToIndex(2), manager.NodeToIndex(5));

            //Constraint variable
            //x = solver.MakeIntVar(day.Vehicles.Count, day.Vehicles.Count, "x");

            //Number of vehicle restriction - old version
            //solver.Add(x < 7);

            //Number of vehicle restriction -new version
            //y = solver.MakeIntVar(routing.Vehicles(), routing.Vehicles(), "y");
            //solver.Add(y <= VCMinMax[1]);//Reduntant

            // Solve the problem.
            solution = routing.SolveWithParameters(searchParameters);



            day.LocationDropped = false;

            // Display dropped nodes.
            List <int> droppedNodes = new List <int>();

            for (int index = 0; index < routing.Size(); ++index)
            {
                if (routing.IsStart(index) || routing.IsEnd(index))
                {
                    continue;
                }
                if (solution.Value(routing.NextVar(index)) == index)
                {
                    droppedNodes.Add(manager.IndexToNode((int)index));
                    day.LocationDropped = true;
                }
            }
            day.DroppedLocations.Clear();
            Console.WriteLine(day.Locations.ElementAt(0).Name);
            if (droppedNodes != null)
            {
                foreach (var item in droppedNodes)
                {
                    Location location = LocationDB.Locations.Where(x => x.Position.strPos_ == day.Addresses[item]).ToList().ElementAt(0);

                    if (location != null)
                    {
                        Console.WriteLine(location.Name);
                        day.DroppedLocations.Add(location);
                    }
                }
            }
            List <int> AssignedNodes = new List <int>();

            Console.WriteLine(manager.GetNumberOfNodes());


            //Inspect Infeasable Nodes
            for (int i = 0; i < day.Vehicles.Count; i++)
            {
                var index = routing.Start(i);
                int j     = 0;
                while (routing.IsEnd(index) == false)
                {
                    j++;

                    index = solution.Value(routing.NextVar(index));
                }
                if (j == 1)
                {
                    day.InfeasibleNodes = true;
                    foreach (var item in day.DroppedLocations)
                    {
                        LocationDB.Locations.Where(x => x.ClientRef == item.ClientRef).ToList().ElementAt(0).Infeasible = true;
                    }
                    if (day.Vehicles.Count - 1 >= 1)
                    {
                        day.SetVehicleNumber(day.Vehicles.Count - 1);
                        day.ResetResults();

                        vrpProblem.SolveVrpProblem(day, cfg, vrpProblem, dataOutput, VCMinMax);
                    }
                    return;
                }
            }

            // Inspect solution.
            day.TotalDur = 0;
            day.MinDur   = 100000;
            for (int i = 0; i < day.Vehicles.Count; i++)
            {
                long routeDuration = 0;

                var index = routing.Start(i);

                while (routing.IsEnd(index) == false)
                {
                    var previousIndex = index;

                    index = solution.Value(routing.NextVar(index));

                    routeDuration += routing.GetArcCostForVehicle(previousIndex, index, 0);
                }
                day.TotalDur += routeDuration;
                day.MaxDur    = Math.Max(routeDuration, day.MaxDur);
                day.MinDur    = Math.Min(routeDuration, day.MinDur);
            }
            day.AvgDur = day.TotalDur / day.Vehicles.Count;
        }
コード例 #15
0
ファイル: Program.cs プロジェクト: paneee/SWay
        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();
        }
コード例 #16
0
    /// <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]
    }
コード例 #17
0
        public IList <string> GetShortestOptimizedRoute([FromBody] GeographicViewModel geographicVM)
        {
            // double[] latitude = { 13.121329, 13.065150, 13.024346, 13.027691, 12.913887, 12.915754, 12.962431, 12.890461, 12.907220, 12.954234, 13.026996, 13.041044, 13.001573 };
            //double[] longitude = { 80.029049, 80.128613, 79.909573, 80.259762, 80.253067, 80.192041, 80.253839, 80.097198, 80.142088, 80.188437, 80.107756, 80.234957, 80.257616 };
            string[] city_names = { "Thirunindravur", "Thiruverkadu", "Senkadu", "Milapore", "VGP Golden", "Medavakkam", "Palavakkam", "Vandalur", "Selaiyur", "Kelkattalai", "Mangadu", "TNagar", "Adyar" };

            double[] latitudeandLongitude;
            var      sCoordlatitudeandLongitude = new List <string>();
            var      finallatitudeandLongitude  = new List <string>();

            distanceArray = new double[geographicVM.Latitude.Length, geographicVM.Longitude.Length];
            var k = 0;

            for (int i = 0; i < geographicVM.Latitude.Length; i++)
            {
                for (int j = 0; j < geographicVM.Longitude.Length; j++)
                {
                    var sCoord = new GeoCoordinate(geographicVM.Latitude[i], geographicVM.Longitude[i]);
                    var eCoord = new GeoCoordinate(geographicVM.Latitude[j], geographicVM.Longitude[j]);
                    if (i == j)
                    {
                        if (i % 2 == 0)
                        {
                            sCoordlatitudeandLongitude.Add(sCoord.ToString() + ",D" + "," + k++);
                        }
                        else
                        {
                            sCoordlatitudeandLongitude.Add(sCoord.ToString() + ",P" + "," + k);
                        }
                    }

                    var text = sCoord.GetDistanceTo(eCoord) / 1609.344;
                    distanceArray[i, j] = Math.Round(text);
                }
            }
            double[,] costs = distanceArray;

            int          num_locations = city_names.Length;
            RoutingModel routingModel  = new RoutingModel(geographicVM.Latitude.Length, 1, 0);

            Solver solver = routingModel.solver();


            string rank_name = "rank";

            routingModel.AddConstantDimension(1, geographicVM.Latitude.Length, true, rank_name);
            var rank_dimension = routingModel.GetDimensionOrDie(rank_name);

            //Constraint MinneapolisBeforeNewYork = solver.MakeLess(routingModel.CumulVar(highPriorityVarIndex, rank_name), routingModel.CumulVar(lowPriorityVarIndex, rank_name));

            /* Later needs to be worked on the Constraint for the  Multiple Pickups before Delivery */
            //Constraint test = routingModel.CumulVar(3, rank_name) < routingModel.CumulVar(13, rank_name);
            //solver.Add(test);


            RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters();

            search_parameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.LocalCheapestArc;
            NodeEvaluator2 cost_between_nodes = new Cost(costs);

            routingModel.SetArcCostEvaluatorOfAllVehicles(cost_between_nodes); //oder SetVehicleCost wenn Fahrzeuge unterschiedliche Kostenmatrix haben

            StringBuilder route = new StringBuilder();

            Assignment assignment = routingModel.SolveWithParameters(search_parameters);

            if (assignment != null)
            {
                Console.WriteLine("Total distance: " + assignment.ObjectiveValue().ToString() + "miles");

                long index = routingModel.Start(0); //vehicle 0

                route.Append("Route: ");

                do
                {
                    //route.Append(city_names[routingModel.IndexToNode(index)] + " -> ");
                    finallatitudeandLongitude.Add(sCoordlatitudeandLongitude[routingModel.IndexToNode(index)]);
                    index = assignment.Value(routingModel.NextVar(index));
                }while (!routingModel.IsEnd(index));
                // route.Append(city_names[routingModel.IndexToNode(index)]);
                finallatitudeandLongitude.Add(sCoordlatitudeandLongitude[routingModel.IndexToNode(index)]);
            }


            return(finallatitudeandLongitude);
        }
コード例 #18
0
        public FeatureCollection Solve(FeatureCollection input)
        {
            if (!input.Features.TrueForAll(x => x.Geometry is Point))
            {
                throw new ArgumentException("All Feature Geometries must be of type Point");
            }

            int         numberOfNodes   = input.Features.Count;
            var         starts          = new List <int>();
            var         ends            = new List <int>();
            int         numberOfDrivers = 0;
            List <long> capacities      = new List <long>();

            foreach (var driverNode in input.Features.Where(x => x.Properties.ContainsKey("driverId")))
            {
                numberOfDrivers += 1;
                capacities.Add(3);
                starts.Add(input.Features.IndexOf(driverNode));
                ends.Add(input.Features.IndexOf(driverNode));
            }

            var routing = new RoutingModel(numberOfNodes, numberOfDrivers, starts.ToArray(), ends.ToArray());

            AddPickupAndDropoff(input, routing);
            var evaluator = SetCost(input, routing);

            //SetCapacity(input, routing, capacities.ToArray());
            AddTimeDimension(input, routing, evaluator);

            var parameters = RoutingModel.DefaultSearchParameters();

            parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;

            // The problem is solved here
            var solution = routing.SolveWithParameters(parameters);

            var output = new FeatureCollection(input.Features);

            for (int routeNumber = 0; routeNumber < numberOfDrivers; routeNumber++)
            {
                var    positions   = new List <IPosition>();
                string strokeColor = "";
                for (long node = routing.Start(routeNumber); !routing.IsEnd(node); node = solution.Value(routing.NextVar(node)))
                {
                    var feature = input.Features[(int)node];
                    var point   = (Point)feature.Geometry;
                    positions.Add(point.Coordinates);
                    if (feature.Properties.ContainsKey("marker-color"))
                    {
                        strokeColor = (string)feature.Properties["marker-color"];
                    }
                }
                if (positions.Count >= 2)
                {
                    var properties = new Dictionary <string, object>
                    {
                        ["stroke"] = strokeColor
                    };
                    output.Features.Add(new Feature(new LineString(positions), properties));
                }
            }

            return(output);
        }
コード例 #19
0
        // <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);
        }
コード例 #20
0
    public static void Main(String[] args)
    {
        // Instantiate the data problem.
        DataModel data = new DataModel();

        // Create Routing Index Manager
        RoutingIndexManager manager = new RoutingIndexManager(
            data.DistanceMatrix.GetLength(0),
            data.VehicleNumber,
            data.Depot);

        // Create Routing Model.
        RoutingModel routing = new RoutingModel(manager);

        // Create and register a transit callback.
        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]);
        }
            );

        // Define cost of each arc.
        routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

        // Add Distance constraint.
        routing.AddDimension(
            transitCallbackIndex,
            0,
            3000,
            true, // start cumul to zero
            "Distance");
        RoutingDimension distanceDimension = routing.GetMutableDimension("Distance");

        distanceDimension.SetGlobalSpanCostCoefficient(100);

        // Define Transportation Requests.
        Solver solver = routing.solver();

        for (int i = 0; i < data.PickupsDeliveries.GetLength(0); i++)
        {
            long pickupIndex   = manager.NodeToIndex(data.PickupsDeliveries[i][0]);
            long deliveryIndex = manager.NodeToIndex(data.PickupsDeliveries[i][1]);
            routing.AddPickupAndDelivery(pickupIndex, deliveryIndex);
            solver.Add(solver.MakeEquality(
                           routing.VehicleVar(pickupIndex),
                           routing.VehicleVar(deliveryIndex)));
            solver.Add(solver.MakeLessOrEqual(
                           distanceDimension.CumulVar(pickupIndex),
                           distanceDimension.CumulVar(deliveryIndex)));
        }

        // 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);

        // Print solution on console.
        PrintSolution(data, routing, manager, solution);
    }
コード例 #21
0
        private static void Test()
        {
            string[] city_names = { "New York", "Los Angeles",   "Chicago",   "Minneapolis", "Denver",  "Dallas", "Seattle",
                                    "Boston",   "San Francisco", "St. Louis", "Houston",     "Phoenix", "Salt Lake City" };

            int tsp_size = city_names.Length;

            // The number of routes, which is 1 in the TSP.
            int num_routes = 1;

            // The depot is the starting node of the route.
            int depot = 0;



            // Create routing model
            if (tsp_size > 0)
            {
                RoutingModel routing           = new RoutingModel(tsp_size, num_routes, 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;

                // Setting the cost function

                // Create the distance callback, which takes two arguments (the from and to node indices)
                // and returns the distance between these nodes.

                //  routing.SetCost(new ConstantCallback());

                routing.SetArcCostEvaluatorOfAllVehicles(new DistanceCallback());

                var assignment = routing.SolveWithParameters(search_parameters);


                Console.WriteLine("Status = {0}", routing.Status());

                if (assignment != null)
                {
                    // Solution cost.
                    Console.WriteLine("Total distance: = {0}", assignment.ObjectiveValue());
                    // Inspect solution.

                    // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
                    int route_number = 0;

                    long index = routing.Start(route_number); // Index of the variable for the starting node.

                    string route = string.Empty;

                    while (!routing.IsEnd(index))
                    {
                        // Convert variable indices to node indices in the displayed route.

                        route += city_names[routing.IndexToNode(index)] + " -> ";

                        index = assignment.Value(routing.NextVar(index));

                        route += city_names[routing.IndexToNode(index)];
                    }


                    Console.WriteLine($"Route: {route}");
                }
                else
                {
                    Console.WriteLine("No solution found.");
                }
            }
        }
コード例 #22
0
        // <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)))
                             );
            }
        }
コード例 #23
0
        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.");
                }
            }
        }
コード例 #24
0
    /// <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));
        }