void Solve(int limit) { // Declares the optimization model. LSModel model = localsolver.GetModel(); trucksUsed = new LSExpression[nbTrucks]; customersSequences = new LSExpression[nbTrucks]; routeDistances = new LSExpression[nbTrucks]; // Sequence of customers visited by each truck. for (int k = 0; k < nbTrucks; k++) { customersSequences[k] = model.List(nbCustomers); } // All customers must be visited by the trucks model.Constraint(model.Partition(customersSequences)); // Create demands and distances as arrays to be able to access it with an "at" operator LSExpression demandsArray = model.Array(demands); LSExpression distanceWarehouseArray = model.Array(distanceWarehouses); LSExpression distanceArray = model.Array(distanceMatrix); for (int k = 0; k < nbTrucks; k++) { LSExpression sequence = customersSequences[k]; LSExpression c = model.Count(sequence); // A truck is used if it visits at least one customer trucksUsed[k] = c > 0; // The quantity needed in each route must not exceed the truck capacity LSExpression demandSelector = model.Function(i => demandsArray[sequence[i]]); LSExpression routeQuantity = model.Sum(model.Range(0, c), demandSelector); model.Constraint(routeQuantity <= truckCapacity); // Distance travelled by truck k LSExpression distSelector = model.Function(i => distanceArray[sequence[i - 1], sequence[i]]); routeDistances[k] = model.Sum(model.Range(1, c), distSelector) + model.If(c > 0, distanceWarehouseArray[sequence[0]] + distanceWarehouseArray[sequence[c - 1]], 0); } nbTrucksUsed = model.Sum(trucksUsed); totalDistance = model.Sum(routeDistances); // Objective: minimize the number of trucks used, then minimize the distance traveled model.Minimize(nbTrucksUsed); model.Minimize(totalDistance); model.Close(); // Parameterizes the solver. LSPhase phase = localsolver.CreatePhase(); phase.SetTimeLimit(limit); localsolver.Solve(); }
/// <summary> /// Sets the SantaVisitStartingTimes variable /// </summary> /// <param name="day"></param> /// <param name="santa"></param> public void SetVisitStartingTimes(int day, int santa) { var s = GetSantaId(day, santa); var sequence = solverVariables.VisitSequences[s]; var c = model.Count(sequence); var visitStartingTimeSelector = model.Function((i, prev) => model.If(i == 0, solverVariables.OptimizationInput.Days[day].from + solverVariables.SantaWaitBeforeStart[s] + solverVariables.DistanceFromHomeArray[sequence[i]], prev + solverVariables.VisitDurationArray[sequence[i - 1]] + solverVariables.DistanceArray[sequence[i - 1], sequence[i]] + solverVariables.SantaWaitBetweenVisitArray[s][sequence[i]] ) ); var visitStartingTime = model.Array(model.Range(0, c), visitStartingTimeSelector); solverVariables.SantaVisitStartingTimes[s] = visitStartingTime; }
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()); }