private OptimizationResult GetModel() { // unavailable var unavailableDay1Before = (int.MinValue, day1Start); var unavailableBetween = (day1End, day2Start); var unavailableDay2After = (day2End, int.MaxValue); var input = new OptimizationInput(); { input.Santas = new Santa[] { new Santa { Id = 100, }, new Santa { Id = 200, }, }; input.Visits = new Visit[] { new Visit { Id = 0, Duration = duration1, Desired = new(int, int)[]
public static GoogleRoutingConfig GetDefault(OptimizationInput input) { return(new GoogleRoutingConfig { MaxNumberOfAdditionalSantas = input.NumberOfSantas(), Mode = input.NumberOfVisits() <= 50 ? SolvingMode.Default : SolvingMode.Fast, }); }
public GenAlgConfig(OptimizationInput input, int maxNumberOfAdditionalSantas = 0) { if (maxNumberOfAdditionalSantas < 0) { throw new ArgumentOutOfRangeException(nameof(maxNumberOfAdditionalSantas), maxNumberOfAdditionalSantas, "must not be negative"); } PopulationSize = CalculatePopulationSize(input); MaxNumberOfSantas = input.Santas.Length + maxNumberOfAdditionalSantas; }
/// <summary> /// /// </summary> /// <param name="input"></param> /// <param name="config"></param> public ParallelGenAlgSolver(OptimizationInput input, ParallelGenAlgConfig config) { if (!config.GenAlgConfig.IsValid()) { throw new ArgumentException("the given GenAlgConfig must not be invalid", nameof(config)); } this.input = input; this.config = config; }
/// <summary> /// /// </summary> /// <param name="input"></param> /// <param name="config"></param> public GenAlgSolver(OptimizationInput input, GenAlgConfig config) { if (!config.IsValid()) { throw new ArgumentException("must not be invalid", "config"); } this.input = input; this.config = config; }
/// <summary> /// Instantiates a new LocalSolver.Solver class with the given optimization input /// </summary> /// <param name="input">the optimization input being used to solve the problem</param> /// <param name="config">config for this solver</param> public Solver(OptimizationInput input, LocalSolverConfig config) { if (config.MaxNumberOfAdditionalSantas < 0) { throw new ArgumentOutOfRangeException(nameof(config), config.MaxNumberOfAdditionalSantas, $"{nameof(config.MaxNumberOfAdditionalSantas)} must not be negative"); } this.input = input; this.config = config; }
public Task <OptimizationOutput> OptimizeAsync(OptimizationInput input, CancellationToken cancellationToken = default(CancellationToken)) { if (input == null) { throw new ArgumentNullException(nameof(input)); } var inputJson = JsonConvert.SerializeObject(input, JsonSerializerSettings); return(OptimizeAsync(inputJson, cancellationToken)); }
public static RoutingData Convert(OptimizationInput input, int maxNumberOfAdditionalSantas) { var data = new RoutingData(input); CreateSantas(data, maxNumberOfAdditionalSantas); CreateVisits(data); CreateUnavailable(data); CreateDesired(data); CreateStartEnd(data); return data; }
public RepairOperation(OptimizationInput input, Dictionary <int, int> alleleToVisitIdMapping) { if (alleleToVisitIdMapping == null) { throw new ArgumentNullException(); } this.input = input; needRepair = input.Visits.Any(v => v.IsBreak); breakMapping = new Dictionary <int, int[]>(); if (needRepair) { CreateBreakMapping(alleleToVisitIdMapping); } }
private static int CalculatePopulationSize(OptimizationInput input) { const int sizeDefault = 262144; const int sizeBigger = 16; // number of visits var x = input.Visits.Count(v => !v.IsBreak) + input.Visits.Count(v => v.IsBreak) * input.Days.Length; // PopulationSize double y; // x -> y // 10 -> 262144 // 20 -> 262144 // 31 -> 262144 // 34 -> 262144 // 50 -> 131072 // 100 -> 16384 // 200 -> 16 // 1000 -> 16 if (x <= 34) // [-inf,34] { y = sizeDefault; } else if (x > 34 && x <= 50) // (34,50] { // linear interpolation // generated with https://mycurvefit.com/ // linear fit method: linear regression // y = -8192*x + 540672 y = -8192 * x + 540672; } else if (x > 50 && x < 200) // (50,200) { // non-linear approximation // generated with https://mycurvefit.com/ // non-linear fit method: exponential basic // y = -250.92 + 1036717 * e ^ (-0.0413231 * x) y = Math.Round(-250.92 + 1036717 * Math.Pow(Math.E, (-0.0413231 * x))); } else // [200,inf] { y = sizeBigger; } return((int)y); }
public static ISolver CreateSolver(OptimizationInput input, ISolverConfig solverConfig) { switch (solverConfig) { case ILPConfig ilp: return(new ILPSolver(input, ilp)); case ManualConfig manual: return(new ManualSolver(input, manual)); case LocalSolverConfig ls: return(new Solver(input, ls)); case ParallelGenAlgConfig pga: return(new ParallelGenAlgSolver(input, pga)); case GoogleRoutingConfig gr: return(new GoogleRoutingSolver(input, gr)); } return(null); }
public VRPCallbackSolver(OptimizationInput input) { this.input = input; visitDurations = input.Visits.Select(v => v.Duration).Prepend(0).ToArray(); distances = new int[input.Visits.Length + 1, input.Visits.Length + 1]; for (int i = 1; i < distances.GetLength(0); i++) { for (int j = 1; j < distances.GetLength(1); j++) { distances[i, j] = input.RouteCosts[i - 1, j - 1]; } } for (int i = 1; i < distances.GetLength(0); i++) { distances[i, 0] = input.Visits[i - 1].WayCostToHome; distances[0, i] = input.Visits[i - 1].WayCostFromHome; } distances[0, 0] = 0; }
private static Dictionary <int, int> CreateAlleles(OptimizationInput input) { var alleleToVisitIdMapping = new Dictionary <int, int>(); // normal visits foreach (var visitId in input.Visits.Where(v => !v.IsBreak).Select(v => v.Id)) { alleleToVisitIdMapping.Add(visitId, visitId); } // breaks var nextVisitId = input.Visits.Select(v => v.Id).Append(0).Max() + 1; foreach (var breakId in input.Visits.Where(v => v.IsBreak).Select(v => v.Id)) { alleleToVisitIdMapping.Add(breakId, breakId); foreach (var _ in input.Days.Skip(1)) { alleleToVisitIdMapping.Add(nextVisitId++, breakId); } } return(alleleToVisitIdMapping); }
/// <summary> /// Returns generated genotypes and mapping from allele to visitId. /// The returned Genotypes are already repaired. /// </summary> /// <param name="input"></param> /// <param name="numberOfIndividuals"></param> /// <param name="maxNumberOfSantas"></param> /// <returns>Population and the AlleleToVisitIdMapping</returns> public (List <Genotype>, Dictionary <int, int>) Generate(OptimizationInput input, int numberOfIndividuals, int maxNumberOfSantas) { var numberOfSeparators = input.Days.Length * maxNumberOfSantas - 1; var alleleToVisitIdMapping = CreateAlleles(input); var elements = new Genotype(); elements.AddRange(alleleToVisitIdMapping.Keys); elements.AddRange(Enumerable.Range(-numberOfSeparators, numberOfSeparators)); var repairOperation = new RepairOperation(input, alleleToVisitIdMapping); var population = new List <Genotype>(numberOfIndividuals); for (int i = 0; i < numberOfIndividuals; i++) { elements.Shuffle(random); var genotype = new Genotype(elements); repairOperation.Repair(genotype); population.Add(genotype); } return(population, alleleToVisitIdMapping); }
/// <summary> /// Creates a new solver /// </summary> /// <param name="input"></param> /// <param name="vrpTimeLimitFactor"></param> public Solver(OptimizationInput input, double vrpTimeLimitFactor, string name = "") { this.input = input; this.vrpTimeLimitFactor = vrpTimeLimitFactor; this.name = name; visitDurations = input.Visits.Select(v => v.Duration).Prepend(0).ToArray(); distances = new int[input.Visits.Length + 1, input.Visits.Length + 1]; for (int i = 1; i < distances.GetLength(0); i++) { for (int j = 1; j < distances.GetLength(1); j++) { distances[i, j] = input.RouteCosts[i - 1, j - 1]; } } for (int i = 1; i < distances.GetLength(0); i++) { distances[i, 0] = input.Visits[i - 1].WayCostToHome; distances[0, i] = input.Visits[i - 1].WayCostFromHome; } distances[0, 0] = 0; }
/// <summary> /// Only use this, if you really need to. /// </summary> /// <param name="maxNumberOfSantas"></param> /// <param name="maxNumberOfGenerations"></param> /// <param name="populationSize"></param> public GenAlgConfig(OptimizationInput input, int maxNumberOfAdditionalSantas, int maxNumberOfGenerations, int populationSize) { MaxNumberOfSantas = input.Santas.Length + maxNumberOfAdditionalSantas; MaxNumberOfGenerations = maxNumberOfGenerations; PopulationSize = populationSize; }
public ManualSolver(OptimizationInput input, ManualConfig config) { this.input = input; this.config = config; }
public SolverVariables(LSModel model, int numberOfRoutes, List <Visit> visits, int[,] routeCosts, OptimizationInput optimizationInput, int numberOfFakeSantas) { Model = model; Visits = visits; NumberOfRoutes = numberOfRoutes; OptimizationInput = optimizationInput; NumberOfFakeSantas = numberOfFakeSantas; NumberOfSantas = optimizationInput.Santas.Length; SantaUsed = new LSExpression[numberOfRoutes]; VisitSequences = new LSExpression[numberOfRoutes + 1]; SantaWalkingTime = new LSExpression[numberOfRoutes]; SantaRouteTime = new LSExpression[numberOfRoutes]; SantaVisitDurations = new LSExpression[numberOfRoutes]; SantaDesiredDuration = new LSExpression[numberOfRoutes]; SantaUnavailableDuration = new LSExpression[numberOfRoutes]; SantaVisitStartingTimes = new LSExpression[numberOfRoutes]; SantaOvertime = new LSExpression[numberOfRoutes]; SantaWaitBeforeStart = new LSExpression[numberOfRoutes]; SantaWaitBetweenVisit = new LSExpression[numberOfRoutes][]; SantaWaitBetweenVisitArray = new LSExpression[numberOfRoutes]; int numberOfVisits = Visits.Count; var longestDay = OptimizationInput.Days.Max(d => d.to - d.from); for (var k = 0; k < numberOfRoutes; k++) { VisitSequences[k] = Model.List(numberOfVisits); SantaOvertime[k] = Model.Int(0, int.MaxValue); SantaWaitBeforeStart[k] = Model.Int(0, longestDay); SantaWaitBetweenVisit[k] = new LSExpression[numberOfVisits]; for (var i = 0; i < SantaWaitBetweenVisit[k].Length; i++) { SantaWaitBetweenVisit[k][i] = Model.Int(0, longestDay); } SantaWaitBetweenVisitArray[k] = Model.Array(SantaWaitBetweenVisit[k]); } // overflow for unused santa breaks VisitSequences[numberOfRoutes] = model.List(numberOfVisits); DistanceArray = model.Array(RouteCostJagged(Visits, routeCosts)); DistanceFromHomeArray = model.Array(Visits.Select(v => v.WayCostFromHome).ToArray()); DistanceToHomeArray = model.Array(Visits.Select(v => v.WayCostToHome).ToArray()); VisitDurationArray = model.Array(Visits.Select(v => v.Duration).ToArray()); // desired var visitsOnlyDesired = visits .Select(v => // fake arr v.Desired.Length == 0 ? new[] { new[] { -1, -1 } } : v.Desired.Select(d => new[] { d.from, d.to }).ToArray() ) .ToArray(); VisitDesiredArray = model.Array(visitsOnlyDesired); VisitDesiredCountArray = model.Array(visits.Select(v => v.Desired.Length).ToArray()); // unavailable var visitsOnlyUnavailable = visits .Select(v => // fake arr v.Unavailable.Length == 0 ? new[] { new[] { -1, -1 } } : v.Unavailable.Select(d => new[] { d.from, d.to }).ToArray() ) .ToArray(); VisitUnavailableArray = model.Array(visitsOnlyUnavailable); VisitUnavailableCountArray = model.Array(visits.Select(v => v.Unavailable.Length).ToArray()); }
public ILPIp5GurobiSolver(OptimizationInput input, ILPIp5GurobiConfig ip5GurobiConfig) { this.input = input; this.ip5GurobiConfig = ip5GurobiConfig; }
public static ISolverConfig CreateSolverConfig(RouteCalculation routeCalculation, OptimizationInput input) { switch (routeCalculation.Algorithm) { case AlgorithmType.ILP: return (new ILPConfig { TimeSliceDuration = Properties.Settings.Default.TimeSliceDurationSeconds, ClusteringMIPGap = Properties.Settings.Default.MIPGapClustering, ClusteringTimeLimitMiliseconds = (long)(0.7 * routeCalculation.TimeLimitMiliseconds), SchedulingMIPGap = Properties.Settings.Default.MIPGapScheduling, SchedulingTimeLimitMiliseconds = (long)(0.3 * routeCalculation.TimeLimitMiliseconds), }); case AlgorithmType.LocalSolver: return (new LocalSolverConfig { VrpTimeLimitFactor = 0.1, VrptwTimeLimitFactor = 0.8, MaxNumberOfAdditionalSantas = routeCalculation.MaxNumberOfAdditionalSantas, }); case AlgorithmType.GeneticAlgorithm: return (new ParallelGenAlgConfig(new GenAlgConfig(input, routeCalculation.MaxNumberOfAdditionalSantas), Properties.Settings.Default.NumberOfGARuns)); case AlgorithmType.GoogleRouting: return(new GoogleRoutingConfig(routeCalculation.MaxNumberOfAdditionalSantas, SolvingMode.All)); case AlgorithmType.Hybrid: throw new ArgumentException("Algorithm type Hybrid not allowed", nameof(routeCalculation.Algorithm)); default: throw new ArgumentOutOfRangeException(nameof(routeCalculation), routeCalculation, "Unknown AlgorithmType. Cannot create a StartData."); } }
public ILPSolver(OptimizationInput input, ILPConfig config) { this.input = input; this.config = config; }
public RoutingData(OptimizationInput input) { Input = input; }
public OptimizationResult Solve(long timeLimitMilliseconds, EventHandler <ProgressReport> progress, EventHandler <string> consoleProgress) { if (timeLimitMilliseconds < config.ClusteringTimeLimitMiliseconds + config.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, config.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 = config.ClusteringTimeLimitMiliseconds; if (clusteringTimeLimitMiliseconds == 0) { // avoid surpassing timelimit clusteringTimeLimitMiliseconds = timeLimitMilliseconds; } var phase1ResultState = clusteringSolver.Solve(config.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(config.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 Algorithm.Scheduling.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 = config.SchedulingTimeLimitMiliseconds + clusteringExtraTime; if (schedulingTimelimitMiliseconds == 0 && timeLimitMilliseconds != 0) { // avoid surpassing timelimit schedulingTimelimitMiliseconds = Math.Max(1, timeLimitMilliseconds - sw.ElapsedMilliseconds); } var schedulingResultState = schedulingSolver.Solve(config.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 *= config.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); }
public Decoder(OptimizationInput input, Dictionary <int, int> alleleToVisitIdMapping) { this.input = input; this.alleleToVisitIdMapping = alleleToVisitIdMapping; }