/// <summary> /// /// </summary> /// <param name="data"></param> /// <param name="costCoefficient">way will be multiplied with this</param> /// <param name="startCost">Additional cost from home to any visit</param> public CostEvaluator(RoutingData data, int costCoefficient, int startCost) { timeEvaluator = new TimeEvaluator(data); this.data = data; this.costCoefficient = costCoefficient; this.startCost = startCost; }
/// <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)); }