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