public void TestSolve()
        {
            var model       = GetModel();
            var solver      = new SchedulingILPSolver(model);
            var resultState = solver.Solve(0, 60000);
            var actual      = solver.GetResult();
            var expected    = new Route(model.Santas.Length, model.Santas[0].GetLength(0))
            {
                Waypoints = new List <Waypoint>[, ] {
                    {     // santa 1
                        new List <Waypoint>()
                        { // day 1
                            new Waypoint(0, -1),
                            new Waypoint(1, 1),
                            new Waypoint(2, 3),
                            new Waypoint(0, 6),
                        },
                        new List <Waypoint>()
                        { // day 2
                            new Waypoint(0, -1),
                            new Waypoint(3, 1),
                            new Waypoint(4, 4),
                            new Waypoint(0, 7),
                        },
                    },
                }
            };

            Assert.AreEqual(expected, actual);
        }
        public void TestRespectingWay()
        {
            var model           = GetModel();
            var solver          = new SchedulingILPSolver(model);
            var resultState     = solver.Solve(0, 60000);
            var calculatedRoute = solver.GetResult();


            Assert.IsNotNull(calculatedRoute);

            var waypoints = calculatedRoute.Waypoints[0, 0];

            Assert.AreEqual(4, waypoints.Count);
            Assert.AreEqual(0, waypoints[0].Visit);

            // Order may change
            if (waypoints[1].Visit == 1)
            {
                Assert.AreEqual(1, waypoints[1].Visit);
                Assert.AreEqual(2, waypoints[2].Visit);
            }
            else
            {
                Assert.AreEqual(2, waypoints[1].Visit);
                Assert.AreEqual(1, waypoints[2].Visit);
            }

            Assert.AreEqual(-1, waypoints[0].StartTime);
            Assert.AreEqual(1, waypoints[1].StartTime);
            Assert.AreEqual(4, waypoints[2].StartTime);
        }
        public void TestIsFeasible()
        {
            var model = GetModel();
            // shouldn't throw an exception
            var solver      = new SchedulingILPSolver(model);
            var resultState = solver.Solve(0, 60000);

            Assert.AreNotEqual(ResultState.Infeasible, resultState);
        }
        public void TestMultipleSanta()
        {
            var model  = GetModel();
            var solver = new SchedulingILPSolver(model);

            solver.Solve(0, 60000);
            var result = solver.GetResult();



            Assert.AreEqual(2, result.Waypoints.GetLength(0));
            Assert.AreEqual(3, result.Waypoints[0, 0].Count);
            Assert.AreEqual(3, result.Waypoints[1, 0].Count);
        }
        public void TestTargetFunctionWorksCorrect()
        {
            var model  = GetModel();
            var solver = new SchedulingILPSolver(model);

            solver.Solve(0, 60000);
            var result = solver.GetResult();


            var waypoints = result.Waypoints[0, 0];

            // three timeslots in desired should be worth more than one timeslot duration
            Assert.AreEqual(waypoints[1].Visit, 2);
            Assert.AreEqual(waypoints[2].Visit, 1);
        }
Example #6
0
        public void TestMinTimeOnly()
        {
            var model  = GetModel();
            var solver = new SchedulingILPSolver(model);

            solver.Solve(0, 60000);
            var result = solver.GetResult();


            var firstWaypoint = result.Waypoints[0, 0].First();
            var lastWaypoint  = result.Waypoints[0, 0].Last();
            var duration      = lastWaypoint.StartTime - firstWaypoint.StartTime;

            Assert.AreEqual(6, duration);
        }
Example #7
0
        private static void TestSerailDataVisits(string serialDataName, int numberOfRuns = 5)
        {
            var solverInputData = Deserialize(serialDataName);

            for (int i = 1; i <= numberOfRuns; i++)
            {
                var sw = Stopwatch.StartNew();

                var solver = new SchedulingILPSolver(solverInputData);
                solver.Solve(0, 10 * 60 * 1000);
                var route = solver.GetResult();
                sw.Stop();
                ConsoleExt.WriteLine($"{i}/{numberOfRuns}: Elapsed s: {sw.ElapsedMilliseconds / 1000}", OutputColor);
                ConsoleExt.WriteLine($"SolutionVal: {route.SolutionValue}", ConsoleColor.Yellow);
            }
        }
Example #8
0
        public OptimizationResult Solve(long timeLimitMilliseconds, EventHandler <ProgressReport> progress, EventHandler <string> consoleProgress)
        {
            if (timeLimitMilliseconds < ip5GurobiConfig.ClusteringTimeLimitMiliseconds + ip5GurobiConfig.SchedulingTimeLimitMiliseconds)
            {
                throw new ArgumentOutOfRangeException(nameof(timeLimitMilliseconds), timeLimitMilliseconds, "must be at least the sum of ClusteringTimeLimit and SchedulingTimeLimit");
            }

            consoleProgress?.Invoke(this, "Solving started");

            var sw = Stopwatch.StartNew();

            var clusteringSolverVariableBuilder = new ClusteringSolverVariableBuilder(input, ip5GurobiConfig.TimeSliceDuration);
            var clusteringSolverInputData       = clusteringSolverVariableBuilder.Build();
            var clusteringSolver =
                new Algorithm.Clustering.ClusteringILPSolver(clusteringSolverInputData);


#if WriteMPS && DEBUG
            System.IO.File.WriteAllText($@"C:\Temp\iRuettae\ILP\Clustering\{new Guid()}.mps", clusterinSolver.ExportMPS());
#endif
            var clusteringTimeLimitMiliseconds = ip5GurobiConfig.ClusteringTimeLimitMiliseconds;
            if (clusteringTimeLimitMiliseconds == 0)
            {
                // avoid surpassing timelimit
                clusteringTimeLimitMiliseconds = timeLimitMilliseconds;
            }

            var phase1ResultState = clusteringSolver.Solve(ip5GurobiConfig.ClusteringMIPGap, clusteringTimeLimitMiliseconds);
            if (!(new[] { ResultState.Feasible, ResultState.Optimal }).Contains(phase1ResultState))
            {
                return(new OptimizationResult()
                {
                    OptimizationInput = input,
                    Routes = new Route[] { },
                    TimeElapsed = sw.ElapsedMilliseconds / 1000,
                });
            }

            var phase1Result = clusteringSolver.GetResult();
            progress?.Invoke(this, new ProgressReport(0.5));
            consoleProgress?.Invoke(this, "Clustering done");
            consoleProgress?.Invoke(this, $"Clustering Result: {phase1Result}");


            var schedulingSovlerVariableBuilders = new List <SchedulingSolverVariableBuilder>();
            foreach (var santa in Enumerable.Range(0, phase1Result.Waypoints.GetLength(0)))
            {
                foreach (var day in Enumerable.Range(0, phase1Result.Waypoints.GetLength(1)))
                {
                    var cluster = phase1Result.Waypoints[santa, day];
                    var schedulingOptimizationInput = new OptimizationInput
                    {
                        Visits     = input.Visits.Where(v => cluster.Select(w => w.Visit - 1).Contains(v.Id)).ToArray(),
                        Santas     = new[] { input.Santas[santa] },
                        Days       = new[] { input.Days[day] },
                        RouteCosts = input.RouteCosts,
                    };

                    schedulingSovlerVariableBuilders.Add(new SchedulingSolverVariableBuilder(ip5GurobiConfig.TimeSliceDuration, schedulingOptimizationInput, cluster.OrderBy(wp => wp.StartTime).Select(wp => wp.Visit).ToArray()));
                }
            }

            var schedulingInputVariables = schedulingSovlerVariableBuilders
                                           .Where(vb => vb.Visits != null && vb.Visits.Count > 1)
                                           .Select(vb => vb.Build());


            var routeResults = schedulingInputVariables
                               .AsParallel()
                               .Select(schedulingInputVariable =>
            {
                var schedulingSolver = new SchedulingILPSolver(schedulingInputVariable);

#if WriteMPS && DEBUG
                System.IO.File.WriteAllText($@"C:\Temp\iRuettae\ILP\Scheduling\{new Guid()}.mps", schedulingSolver.ExportMPS());
#endif


                var clusteringExtraTime            = Math.Max(0, clusteringTimeLimitMiliseconds - sw.ElapsedMilliseconds);
                var schedulingTimelimitMiliseconds = ip5GurobiConfig.SchedulingTimeLimitMiliseconds + clusteringExtraTime;
                if (schedulingTimelimitMiliseconds == 0 && timeLimitMilliseconds != 0)
                {
                    // avoid surpassing timelimit
                    schedulingTimelimitMiliseconds = Math.Max(1, timeLimitMilliseconds - sw.ElapsedMilliseconds);
                }

                var schedulingResultState = schedulingSolver.Solve(ip5GurobiConfig.SchedulingMIPGap, schedulingTimelimitMiliseconds);
                if (!(new[] { ResultState.Feasible, ResultState.Optimal }).Contains(schedulingResultState))
                {
                    var realWaypointList = new List <Algorithm.Waypoint>();

                    // take presolved and return it
                    for (int i = 0; i < schedulingInputVariable.Presolved.Length; i++)
                    {
                        var i1        = i;
                        var currVisit = input.Visits.FirstOrDefault(v => v.Id == schedulingInputVariable.Presolved[i1] - 1);

                        var timeStamp = schedulingInputVariable.DayStarts[0];
                        if (i > 0)
                        {
                            var lastVisit = input.Visits.FirstOrDefault(v => v.Id == schedulingInputVariable.Presolved[i - 1] - 1);

                            timeStamp  = realWaypointList.Last().StartTime + lastVisit.Duration;
                            timeStamp += i > 1
                                    ? input.RouteCosts[lastVisit.Id, currVisit.Id]
                                    : currVisit.WayCostFromHome;
                        }

                        realWaypointList.Add(new Algorithm.Waypoint(currVisit.Equals(default(Visit)) ? Constants.VisitIdHome : currVisit.Id,
                                                                    timeStamp));
                    }
                    var absolutlyLastVisit = input.Visits.FirstOrDefault(v => v.Id == schedulingInputVariable.Presolved[schedulingInputVariable.Presolved.Length - 1] - 1);
                    realWaypointList.Add(new Algorithm.Waypoint(Constants.VisitIdHome, realWaypointList.Last().StartTime + absolutlyLastVisit.Duration + absolutlyLastVisit.WayCostToHome));

                    return(new Algorithm.Route(1, 1)
                    {
                        SantaIds = schedulingInputVariable.SantaIds,
                        Waypoints = new[, ]
                        {
                            { realWaypointList }
                        }
                    });
                }

                var route = schedulingSolver.GetResult();

                for (int i = 0; i < route.Waypoints.GetLength(0); i++)
                {
                    for (int j = 0; j < route.Waypoints.GetLength(1); j++)
                    {
                        var realWaypointList = new List <Algorithm.Waypoint>();

                        var waypointList = route.Waypoints[i, j];
                        // copy for later lambda expression
                        var jCopy = j;
                        waypointList.ForEach(wp =>
                        {
                            wp.Visit = wp.Visit == 0
                                    ? Constants.VisitIdHome
                                    : schedulingInputVariable.VisitIds[wp.Visit - 1];
                            wp.StartTime  = Math.Max(wp.StartTime, 0);
                            wp.StartTime *= ip5GurobiConfig.TimeSliceDuration;
                            wp.StartTime += schedulingInputVariable.DayStarts[jCopy];
                            realWaypointList.Add(wp);
                        });
                        route.Waypoints[i, j] = realWaypointList;
                    }
                }
                return(route);
            })
                               .ToList();

            progress?.Invoke(this, new ProgressReport(0.99));
            consoleProgress?.Invoke(this, "Scheduling done");
            consoleProgress?.Invoke(this, $"Scheduling Result:{Environment.NewLine}" +
                                    routeResults.Where(r => r != null).Select(r => r.ToString()).Aggregate((acc, c) => acc + Environment.NewLine + c));

            // construct new output elem
            var optimizationResult = new OptimizationResult()
            {
                OptimizationInput = input,
                Routes            = routeResults.Select(r => r != null ? new Route
                {
                    SantaId   = r.SantaIds[0],
                    Waypoints = r.Waypoints[0, 0].Select(origWp => new Waypoint
                    {
                        VisitId   = origWp.Visit,
                        StartTime = origWp.StartTime
                    }).ToArray(),
                } : new Route()).ToArray(),
            };

            progress?.Invoke(this, new ProgressReport(1));

            // assign total elapsed time
            sw.Stop();
            optimizationResult.TimeElapsed = sw.ElapsedMilliseconds / 1000;
            return(optimizationResult);
        }