public void TestSolutions5NotClosedNotFixed() { RandomGeneratorExtensions.GetGetNewRandom = () => new RandomGenerator(4541247); // create problem. var problem = TSPHelper.CreateDirectedTSP(0, 5, 10, 1); problem.Weights.SetWeight(0, 1, 1); problem.Weights.SetWeight(1, 2, 1); problem.Weights.SetWeight(2, 3, 1); problem.Weights.SetWeight(3, 4, 1); problem.Weights.SetWeight(4, 0, 1); // create the solver. var solver = new HillClimbing3OptSolver(); for (int i = 0; i < 10; i++) { // generate solution. float fitness; var solution = solver.Solve(problem, new TSPObjective(), out fitness); // test contents. Assert.IsTrue(fitness <= 7); var solutionList = new List <int>(solution); Assert.AreEqual(0, DirectedHelper.ExtractId(solutionList[0])); Assert.AreEqual(5, solutionList.Count); } }
/// <summary> /// Builds the result route in segments divided by routes between customers. /// </summary> /// <returns></returns> public static List <Result <Route> > TryBuildRoutes <T>(this IDirectedWeightMatrixAlgorithm <T> algorithm, Tour tour) { var routes = new List <Result <Route> >(); // TODO: check what to do here, use the cached version or not? var weightHandler = algorithm.Profile.DefaultWeightHandler(algorithm.Router); foreach (var pair in tour.Pairs()) { // TODO: extract more info at once! var pairFromDepartureId = algorithm.SourcePaths[DirectedHelper.ExtractDepartureId(pair.From)]; var pairToArrivalId = algorithm.TargetPaths[DirectedHelper.ExtractArrivalId(pair.To)]; var pairFromEdgeId = algorithm.Router.Db.Network.GetEdges(pairFromDepartureId.From.Vertex).First(x => x.To == pairFromDepartureId.Vertex).IdDirected(); var pairToEdgeId = algorithm.Router.Db.Network.GetEdges(pairToArrivalId.Vertex).First(x => x.To == pairToArrivalId.From.Vertex).IdDirected(); var pairFromId = DirectedHelper.ExtractId(pair.From); var pairToId = DirectedHelper.ExtractId(pair.To); var fromRouterPoint = algorithm.RouterPoints[pairFromId]; var toRouterPoint = algorithm.RouterPoints[pairToId]; var localRouteRaw = algorithm.Router.TryCalculateRaw(algorithm.Profile, weightHandler, pairFromEdgeId, pairToEdgeId, null).Value; localRouteRaw.StripSource(); localRouteRaw.StripTarget(); var localRoute = algorithm.Router.BuildRoute(algorithm.Profile, weightHandler, fromRouterPoint, toRouterPoint, localRouteRaw); routes.Add(localRoute); } return(routes); }
private void Set(TSProblem problem, int directedCustomer, bool value) { if (_dontLook) { var originalId = DirectedHelper.ExtractId(directedCustomer); _dontLookBits[originalId] = value; } }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v1. /// </summary> /// <returns></returns> public bool Try3OptMoves(TSProblem problem, float[][] weights, Tour tour, int v1, out float delta) { // get v_2. var v2 = tour.GetNeigbour(v1); if (v2 < 0) { delta = 0; return(false); } var betweenV2V1 = tour.Between(v2, v1); //var weightV1V2 = weights[v1][v2]; var weightV1V2 = weights.WeightWithoutTurns(v1, v2); if (DirectedHelper.ExtractId(v2) == problem.First && !problem.Last.HasValue) { // set to zero if not closed. weightV1V2 = 0; } int v3 = -1; NearestNeighbours neighbours = null; if (_nearestNeighbours) { neighbours = problem.GetNNearestNeighboursForward(10, DirectedHelper.ExtractId(v1)); } foreach (int v4 in betweenV2V1) { if (v3 >= 0 && v3 != v1) { var v4Id = DirectedHelper.ExtractId(v4); if (!_nearestNeighbours || neighbours.Contains(v4Id)) { //var weightV1V4 = weights[v1][v4]; var weightV1V4 = weights.WeightWithoutTurns(v1, v4); //var weightV3V4 = weights[v3][v4]; var weightV3V4 = weights.WeightWithoutTurns(v3, v4); if (v4Id == problem.First && !problem.Last.HasValue) { // set to zero if not closed. weightV1V4 = 0; weightV3V4 = 0; } var weightV1V2PlusV3V4 = weightV1V2 + weightV3V4; //var weightsV3 = weights[v3]; var weightsV3 = weights[DirectedHelper.ExtractDepartureId(v3)]; if (this.Try3OptMoves(problem, weights, tour, v1, v2, v3, weightsV3, v4, weightV1V2PlusV3V4, weightV1V4, out delta)) { return(true); } } } v3 = v4; } delta = 0; return(false); }
private bool Check(TSProblem problem, int directedCustomer) { if (_dontLook) { var originalId = DirectedHelper.ExtractId(directedCustomer); return(_dontLookBits[originalId]); } return(false); }
public void TestSolution5ClosedFixed() { // create problem. var problem = TSPTWHelper.CreateDirectedTSPTW(0, 4, 5, 10, 1); problem.Windows[1] = new TimeWindow() { Min = 5, Max = 15 }; problem.Windows[2] = new TimeWindow() { Min = 15, Max = 25 }; problem.Windows[3] = new TimeWindow() { Min = 25, Max = 35 }; problem.Windows[4] = new TimeWindow() { Min = 35, Max = 45 }; // create the solver. var solver = new VNSConstructionSolver(); solver.IntermidiateResult += (x) => { var fitness = (new TSPTWFeasibleObjective()).Calculate(problem, x); fitness = fitness + 0; }; for (int i = 0; i < 10; i++) { // generate solution. float fitness; var solution = solver.Solve(problem, new TSPTWFeasibleObjective(), out fitness); // test contents. Assert.AreEqual(0, fitness); var solutionList = new List <int>(); foreach (var directed in solution) { solutionList.Add(DirectedHelper.ExtractId(directed)); } Assert.AreEqual(0, solutionList[0]); Assert.AreEqual(4, solutionList[4]); Assert.IsTrue(solutionList.Remove(0)); Assert.IsTrue(solutionList.Remove(1)); Assert.IsTrue(solutionList.Remove(2)); Assert.IsTrue(solutionList.Remove(3)); Assert.IsTrue(solutionList.Remove(4)); Assert.AreEqual(0, solutionList.Count); } }
public void TestPerturbationSomeViolations() { // set the seed manually. RandomGeneratorExtensions.GetGetNewRandom = () => new RandomGenerator(4541247); // create the perturber. var perturber = new Random1Shift <TSPTWObjective>(); // create a problem where any move would violate windows. var problem = TSPTWHelper.CreateDirectedTSPTW(0, 0, 5, 2, 1); problem.Windows[2] = new TimeWindow() { Min = 4, Max = 5 }; problem.Windows[3] = new TimeWindow() { Min = 6, Max = 7 }; var objective = new TSPTWObjective(); // execute random shifts. for (int i = 0; i < 1000; i++) { // create solution. var solution = new Optimization.Tours.Tour(new int[] { 0, 4, 8, 12, 16 }); var fitnessBefore = objective.Calculate(problem, solution); // shift one customer. float difference; perturber.Apply(problem, objective, solution, out difference); // check if valid solution. var solutionList = new List <int>(); foreach (var directed in solution) { solutionList.Add(DirectedHelper.ExtractId(directed)); } Assert.AreEqual(0, solutionList[0]); Assert.IsTrue(solutionList.Remove(0)); Assert.IsTrue(solutionList.Remove(1)); Assert.IsTrue(solutionList.Remove(2)); Assert.IsTrue(solutionList.Remove(3)); Assert.IsTrue(solutionList.Remove(4)); Assert.AreEqual(0, solutionList.Count); // test if first is still first. Assert.AreEqual(problem.First, solution.First); // calculate expected difference. var fitnessAfter = objective.Calculate(problem, solution); Assert.AreEqual(fitnessAfter - fitnessBefore, difference, 0.0000001); } }
public void TestSolutions5ClosedNotFixed() { RandomGeneratorExtensions.GetGetNewRandom = () => { return(new RandomGenerator(45841647)); }; // create problem. var problem = TSPTWHelper.CreateDirectedTSPTW(0, 0, 5, 10, 1); problem.Windows[1] = new TimeWindow() { Min = 5, Max = 15 }; problem.Windows[2] = new TimeWindow() { Min = 15, Max = 25 }; problem.Windows[3] = new TimeWindow() { Min = 25, Max = 35 }; problem.Windows[4] = new TimeWindow() { Min = 35, Max = 45 }; // create the solver. var solver = new VNDOperator(); for (int i = 0; i < 10; i++) { // generate solution. float fitness; var solution = solver.Solve(problem, new TSPTWObjective(), out fitness); // test contents. Assert.AreEqual(50, fitness, string.Format("Solution was: {0} with fitness {1}.", solution.ToInvariantString(), fitness)); var solutionList = new List <int>(); foreach (var directedId in solution) { solutionList.Add(DirectedHelper.ExtractId(directedId)); } Assert.AreEqual(0, solutionList[0]); Assert.IsTrue(solutionList.Remove(0)); Assert.IsTrue(solutionList.Remove(1)); Assert.IsTrue(solutionList.Remove(2)); Assert.IsTrue(solutionList.Remove(3)); Assert.IsTrue(solutionList.Remove(4)); Assert.AreEqual(0, solutionList.Count); } }
/// <summary> /// Applies this operator. /// </summary> public bool Apply(TSProblem problem, TSPObjective objective, Tour solution, out float delta) { if (problem.Weights.Length <= 2) { delta = 0; return(false); } var before = objective.Calculate(problem, solution); var weights = problem.Weights; var turnPenalties = problem.TurnPenalties; delta = 0; // test switching directions in random order. if (_pool == null || solution.Count != _pool.Size) { // create a new pool. _pool = new RandomPool(solution.Count); } else { // just reset the existing one. _pool.Reset(); } var i = _n; var toInsert = new List <int>(); while (_pool.MoveNext() && i > 0) { i--; var currentId = _pool.Current; var current = solution.GetDirectedId(currentId); if (current != Constants.NOT_SET) { if (current != solution.First && current != solution.Last && solution.Remove(current)) { toInsert.Add(current); } } } foreach (var current in toInsert) { CheapestInsertionDirectedHelper.InsertCheapestDirected(solution, weights, turnPenalties, DirectedHelper.ExtractId(current)); } var after = objective.Calculate(problem, solution); delta = after - before; return(delta < 0); }
public void TestClosed() { var problem = SequenceProblemHelper.Create(new Tour(new int[] { 0, 1, 2, 3 }, 0), 4, 60, 360); var solver = new ConstructionSolver(); var result = solver.Solve(problem, new SequenceDirectedObjective()); Assert.IsNotNull(result); var arr = result.ToArray(); Assert.AreEqual(arr.Length, 4); Assert.AreEqual(0, DirectedHelper.ExtractId(arr[0])); Assert.AreEqual(1, DirectedHelper.ExtractId(arr[1])); Assert.AreEqual(2, DirectedHelper.ExtractId(arr[2])); Assert.AreEqual(3, DirectedHelper.ExtractId(arr[3])); }
/// <summary> /// Solves the given problem. /// </summary> /// <returns></returns> public sealed override Tour Solve(STSProblem problem, STSPObjective objective, out STSPFitness fitness) { // generate empty route based on problem definition. var route = problem.CreateEmptyTour(); fitness = new STSPFitness() { Weight = 0, Customers = 1 }; // generate random pool to select customers from. if (_randomPool == null || _randomPool.Size < problem.Weights.Length) { _randomPool = new RandomPool(problem.Weights.Length / 2); } else { _randomPool.Reset(); } // keep adding customers until no more space is left or no more customers available. while (_randomPool.MoveNext()) { var customer = _randomPool.Current; if (customer == DirectedHelper.ExtractId(route.First) || (route.Last.HasValue && customer == DirectedHelper.ExtractId(route.Last.Value))) { // customer is first or last. continue; } var cost = CheapestInsertionDirectedHelper.InsertCheapestDirected(route, problem.Weights, problem.TurnPenalties, customer, problem.Max - fitness.Weight); if (cost > 0) { fitness.Customers++; fitness.Weight += cost; } } // calculate fitness. fitness = objective.Calculate(problem, route); return(route); }
public void TestSolution5ClosedFixed() { // create problem. var problem = TSPTWHelper.CreateDirectedTSPTW(0, 4, 5, 10, 1); problem.Times.SetWeight(0, 1, 2); problem.Times.SetWeight(1, 2, 2); problem.Times.SetWeight(2, 3, 2); problem.Times.SetWeight(3, 4, 2); problem.Times.SetWeight(4, 0, 2); problem.Windows[2] = new TimeWindow() { Min = 3, Max = 5 }; problem.Windows[4] = new TimeWindow() { Min = 7, Max = 9 }; var objective = new TSPTWObjective(); // create the solver. var solver = new VNSSolver(); for (int i = 0; i < 10; i++) { // generate solution. float fitness; var solution = solver.Solve(problem, objective, out fitness); // test contents. Assert.IsTrue(fitness <= 12); var solutionList = new List <int>(); foreach (var directed in solution) { solutionList.Add(DirectedHelper.ExtractId(directed)); } Assert.AreEqual(5, solutionList.Count); Assert.AreEqual(0, solutionList[0]); Assert.AreEqual(4, solutionList[4]); } }
public void TestSolution1() { // create problem. var problem = TSPTWHelper.CreateDirectedTSPTW(0, 0, 1, 0, 1); var objective = new TSPTWObjective(); // create the solver. var solver = new VNSSolver(); for (int i = 0; i < 10; i++) { // generate solution. float fitness; var solution = solver.Solve(problem, objective, out fitness); // test contents. Assert.IsTrue(fitness < 2); var solutionList = new List <int>(solution); Assert.AreEqual(0, DirectedHelper.ExtractId(solutionList[0])); } }
public void TestSolution1() { RandomGeneratorExtensions.GetGetNewRandom = () => new RandomGenerator(4541247); // create problem. var problem = TSPHelper.CreateDirectedTSP(0, 0, 1, 0, 1); // create the solver. var solver = new HillClimbing3OptSolver(); for (int i = 0; i < 10; i++) { // generate solution. float fitness; var solution = solver.Solve(problem, new TSPObjective(), out fitness); // test contents. Assert.AreEqual(0, fitness); var solutionList = new List <int>(solution); Assert.AreEqual(0, DirectedHelper.ExtractId(solutionList[0])); Assert.AreEqual(1, solutionList.Count); } }
/// <summary> /// Builds the result route in segments divided by routes between customers. /// </summary> /// <returns></returns> public static List <Route> BuildRoutes <T>(this IDirectedWeightMatrixAlgorithm <T> algorithm, Tour tour) { var routes = new List <Route>(); // TODO: check what to do here, use the cached version or not? var weightHandler = algorithm.Profile.DefaultWeightHandler(algorithm.Router); foreach (var pair in tour.Pairs()) { // TODO: extract more info at once! var pairFromDepartureId = algorithm.SourcePaths[DirectedHelper.ExtractDepartureId(pair.From)]; var pairToArrivalId = algorithm.TargetPaths[DirectedHelper.ExtractArrivalId(pair.To)]; var pairFromEdgeId = algorithm.Router.Db.Network.GetEdges(pairFromDepartureId.From.Vertex).First(x => x.To == pairFromDepartureId.Vertex).IdDirected(); var pairToEdgeId = algorithm.Router.Db.Network.GetEdges(pairToArrivalId.Vertex).First(x => x.To == pairToArrivalId.From.Vertex).IdDirected(); var pairFromId = DirectedHelper.ExtractId(pair.From); var pairToId = DirectedHelper.ExtractId(pair.To); var fromRouterPoint = algorithm.RouterPoints[pairFromId]; var toRouterPoint = algorithm.RouterPoints[pairToId]; var localRouteRaw = algorithm.Router.TryCalculateRaw(algorithm.Profile, weightHandler, pairFromEdgeId, pairToEdgeId, null).Value; localRouteRaw.StripSource(); localRouteRaw.StripTarget(); var localRoute = algorithm.Router.BuildRoute(algorithm.Profile, weightHandler, fromRouterPoint, toRouterPoint, localRouteRaw); if (localRoute.IsError) { throw new Itinero.Exceptions.RouteNotFoundException( string.Format("Part of the tour was not found: {0}[{1}] -> {2}[{3}] - {4}.", pair.From, pairFromId, pair.To, pairToId, localRoute.ErrorMessage)); } routes.Add(localRoute.Value); } return(routes); }
public void TestSolutionClosed() { RandomGeneratorExtensions.GetGetNewRandom = () => new RandomGenerator(4541247); // create problem. var problem = STSPHelper.CreateDirectedSTSP(0, 0, 5, 10, 1, 40); // create the solver. var solver = new RandomSolver(); var objective = new STSPObjective(); for (var i = 0; i < 100; i++) { // generate solution. STSPFitness fitness; var solution = solver.Solve(problem, objective, out fitness); // test contents. Assert.AreEqual(problem.First, solution.First); Assert.AreEqual(problem.Last, DirectedHelper.ExtractId(solution.Last.Value)); Assert.AreEqual(solution.Count, fitness.Customers); Assert.IsTrue(40 >= fitness.Weight); } }
/// <summary> /// Returns true if there was an improvement, false otherwise. /// </summary> /// <returns></returns> public bool Apply(TSPTWProblem problem, TSPTWObjective objective, Tour tour, out float delta) { delta = 0; var fitness = objective.Calculate(problem, tour); var customers = new List <int>(problem.Times.Length + 1); customers.AddRange(tour); if (tour.Last == tour.First) { // add last customer at the end if it's the same as the first one. customers.Add(tour.Last.Value); } // select two edges with at least one edge between them at both sides of the tour. var weightBefore = 0f; if (problem.Windows[DirectedHelper.ExtractId(customers[0])].Min > weightBefore) { // wait here! weightBefore = problem.Windows[DirectedHelper.ExtractId(customers[0])].Min; } for (var edge1 = 0; edge1 < customers.Count - 3; edge1++) { // iterate over all from-edges. var edge11 = customers[edge1 + 0]; var edge12 = customers[edge1 + 1]; // extract directional information. int edge11arrivalId, edge11departureId, edge11id, edge11turn; DirectedHelper.ExtractAll(edge11, out edge11arrivalId, out edge11departureId, out edge11id, out edge11turn); int edge12arrivalId, edge12departureId, edge12id, edge12turn; DirectedHelper.ExtractAll(edge12, out edge12arrivalId, out edge12departureId, out edge12id, out edge12turn); for (var edge2 = edge1 + 2; edge2 < customers.Count - 1; edge2++) { // iterate over all possible to-edges given the from-edge. var edge21 = customers[edge2 + 0]; var edge22 = customers[edge2 + 1]; // at this point we have two edges 11->12->...->21->22->... // attempt to reverse the part in between. // 1: calculate the reverse part and check if feasible. // 2: if not feasible then stop. // 3: if feasible then calculate the forward part. // 4: if better then accept the result. // calculate het best possible reverse sequence by switching turns and departure and arrivel id's. // First start by creating a sequence with all turns and arrival and departure id's equal to zero. var part = new List <int>(); part.Add(DirectedHelper.UpdateDepartureOffset(edge11, 0)); for (var b = edge2; b > edge1; b--) { part.Add(DirectedHelper.BuildDirectedId(DirectedHelper.ExtractId(customers[b]), 0)); } part.Add(DirectedHelper.UpdateArrivalOffset(edge22, 0)); var localDelta = this.OptimizePart(problem, part); while (localDelta > 0.1) { localDelta = this.OptimizePart(problem, part); } int violated; float violatedTime, waitTime; var partTime = objective.CalculateTimeForPart(problem, part, weightBefore, out violated, out violatedTime, out waitTime); if (violated == 0) { // new part is feasible, this should be fine. var existingPart = new List <int>(); existingPart.Add(edge11); for (var c = edge1 + 1; c < edge2; c++) { existingPart.Add(customers[c]); } existingPart.Add(edge22); var existingPartTime = objective.CalculateTimeForPart(problem, existingPart, weightBefore, out violated, out violatedTime, out waitTime); if (existingPartTime > partTime) { // an improvment, add the new part. tour.Replace(edge11, part[0]); tour.Replace(edge22, part[part.Count - 1]); for (var c = 1; c < part.Count; c++) { tour.ReplaceEdgeFrom(part[c - 1], part[c]); } var newFitness = objective.Calculate(problem, tour); delta = fitness - newFitness; return(true); } } } // update weight before. weightBefore += problem.Times[edge11arrivalId][edge12arrivalId]; weightBefore += problem.TurnPenalties[edge11turn]; if (problem.Windows[DirectedHelper.ExtractId(customers[edge1])].Min > weightBefore) { // wait here! weightBefore = problem.Windows[DirectedHelper.ExtractId(customers[edge1])].Min; } } return(false); }
/// <summary> /// Optimizes the given part of the tour by choosing the best improvements in either the departureOffset or arrivalOffset of the first/last customers or the turns at any of the intermediate ones. /// </summary> private float OptimizePart(TSPTWProblem problem, List <int> part) { int turn1, arrivalId1, departureId1, id1; int turn2, arrivalId2, departureId2, id2; int turn3, arrivalId3, departureId3, id3; var delta = 0f; // the positive difference. var arrivalIdChange = -1; var departureIdChange = -1; var customerIdx = -1; var customerTurn = -1; // try changing the departureId. DirectedHelper.ExtractAll(part[0], out arrivalId1, out departureId1, out id1, out turn1); DirectedHelper.ExtractAll(part[1], out arrivalId2, out departureId2, out id2, out turn2); var weight = problem.TurnPenalties[turn1]; weight += problem.Times[departureId1][arrivalId2]; var new0 = DirectedHelper.SwitchDepartureOffset(part[0]); DirectedHelper.ExtractAll(new0, out arrivalId3, out departureId3, out id3, out turn3); var newWeight = problem.TurnPenalties[turn3]; newWeight += problem.Times[departureId3][arrivalId2]; if (newWeight < weight) { // there was an improvement found in changing the departure id. departureIdChange = DirectedHelper.ExtractDepartureId(new0); delta = newWeight - weight; } // try changing the arrivalId. DirectedHelper.ExtractAll(part[part.Count - 2], out arrivalId1, out departureId1, out id1, out turn1); DirectedHelper.ExtractAll(part[part.Count - 1], out arrivalId2, out departureId2, out id2, out turn2); weight = problem.TurnPenalties[turn2]; weight += problem.Times[departureId1][arrivalId2]; var newLast = DirectedHelper.SwitchArrivalOffset(part[part.Count - 1]); DirectedHelper.ExtractAll(newLast, out arrivalId3, out departureId3, out id3, out turn3); newWeight = problem.TurnPenalties[turn3]; newWeight += problem.Times[departureId1][arrivalId3]; if (newWeight < weight && delta < (newWeight - weight)) { // there was an improvement found in changing the arrival id. arrivalIdChange = DirectedHelper.ExtractDepartureId(newLast); departureIdChange = -1; delta = newWeight - weight; } for (var c = 1; c < part.Count - 1; c++) { var perviousDepartureId = DirectedHelper.ExtractDepartureId(part[c - 1]); DirectedHelper.ExtractAll(part[c], out arrivalId1, out departureId1, out id1, out turn1); var nextArrivalid = DirectedHelper.ExtractArrivalId(part[c + 1]); weight = problem.Times[perviousDepartureId][arrivalId1]; weight += problem.TurnPenalties[turn1]; weight += problem.Times[departureId1][nextArrivalid]; for (var t = 0; t < 3; t++) { if (t == turn1) { continue; } var newDirectedId = DirectedHelper.BuildDirectedId(id1, t); DirectedHelper.ExtractAll(newDirectedId, out arrivalId2, out departureId2, out id2, out turn2); newWeight = problem.Times[perviousDepartureId][arrivalId2]; newWeight += problem.TurnPenalties[turn2]; newWeight += problem.Times[departureId2][nextArrivalid]; if (newWeight < weight && delta < (newWeight - weight)) { // there was an improvement found in changing the turn. arrivalIdChange = -1; departureIdChange = -1; customerIdx = c; customerTurn = t; delta = newWeight - weight; } } } if (delta > 0) { if (departureIdChange != -1) { part[0] = DirectedHelper.SwitchDepartureOffset(part[0]); } else if (arrivalIdChange != -1) { part[part.Count - 1] = DirectedHelper.SwitchArrivalOffset(part[part.Count - 1]); } else if (customerIdx != -1) { part[customerIdx] = DirectedHelper.BuildDirectedId( DirectedHelper.ExtractId(part[customerIdx]), customerTurn); } } return(delta); }
/// <summary> /// Inserts the given customer at the best location if possible. /// </summary> /// <param name="tour">The tour to insert into.</param> /// <param name="weights">The directed weights.</param> /// <param name="turnPenalties">The turn pentalties.</param> /// <param name="customer">The customer to insert.</param> /// <param name="metric">The metric to use, time, distance or custom.</param> /// <param name="max">The maximum allowed cost of the insertion.</param> public static Weight InsertCheapestDirected(this Tour tour, Weight[][] weights, Weight[] turnPenalties, ProfileMetric metric, int customer, Weight max) { if (tour.Count == 1) { // there is only one customer, the first one in the route. if (tour.First == tour.Last) { // there is one customer but it's both the start and the end. var firstId = DirectedHelper.ExtractId(tour.First); var bestCost = new Weight() { Distance = float.MaxValue, Time = float.MaxValue, Value = float.MaxValue }; var bestDirected1Id = int.MaxValue; var bestDirected2Id = int.MaxValue; for (var turn1 = 0; turn1 < 4; turn1++) { var firstDirectedId = DirectedHelper.BuildDirectedId(firstId, turn1); var firstArrivalId = DirectedHelper.ExtractArrivalId(firstDirectedId); var firstDepartureId = DirectedHelper.ExtractDepartureId(firstDirectedId); for (var turn2 = 0; turn2 < 4; turn2++) { var customerDirectedId = DirectedHelper.BuildDirectedId(customer, turn2); var customerArrivalId = DirectedHelper.ExtractArrivalId(customerDirectedId); var customerDepartureId = DirectedHelper.ExtractDepartureId(customerDirectedId); var weight = turnPenalties[turn1]; weight += turnPenalties[turn2]; weight += weights[firstDepartureId][customerArrivalId]; weight += weights[customerDepartureId][firstArrivalId]; if (bestCost.GetForMetric(metric) > weight.GetForMetric(metric)) { bestDirected1Id = firstDirectedId; bestDirected2Id = customerDirectedId; bestCost = weight; } } } if (bestCost.GetForMetric(metric) <= max.GetForMetric(metric)) { tour.Replace(tour.First, bestDirected1Id); tour.InsertAfter(tour.First, bestDirected2Id); return bestCost; } } else { // there is one customer, the last one is not set. var firstId = DirectedHelper.ExtractId(tour.First); int departureOffset1, arrivalOffset2; var cost = DirectedHelper.CheapestInsert(weights, turnPenalties, firstId, customer, metric, out departureOffset1, out arrivalOffset2); if (cost.GetForMetric(metric) <= max.GetForMetric(metric)) { var newFirst = DirectedHelper.UpdateDepartureOffset( DirectedHelper.BuildDirectedId(firstId, 0), departureOffset1); var customerDirectedId = DirectedHelper.UpdateArrivalOffset( DirectedHelper.BuildDirectedId(customer, 0), arrivalOffset2); tour.Replace(tour.First, newFirst); tour.InsertAfter(tour.First, customerDirectedId); return cost; } } } else { // at least 2 customers already exist, insert a new one in between. var cost = Weight.MaxValue; var departureOffsetFrom = Constants.NOT_SET; var arrivalOffsetTo = Constants.NOT_SET; var turn = Constants.NOT_SET; var location = new Pair(int.MaxValue, int.MaxValue); foreach (var pair in tour.Pairs()) { int departureOffset1, arrivalOffset3, turn2; var fromIsFirst = tour.IsFirst(pair.From); var toIsLast = tour.IsLast(pair.To); var localCost = CheapestInsertionDirectedHelper.CalculateCheapestInsert(weights, turnPenalties, metric, pair.From, customer, pair.To, !fromIsFirst, !toIsLast, out departureOffset1, out arrivalOffset3, out turn2); if (localCost.GetForMetric(metric) < cost.GetForMetric(metric)) { cost = localCost; location = pair; departureOffsetFrom = departureOffset1; arrivalOffsetTo = arrivalOffset3; turn = turn2; } } if (cost.GetForMetric(metric) <= max.GetForMetric(metric)) { var directedId = DirectedHelper.BuildDirectedId(customer, turn); tour.InsertAfter(location.From, directedId); // update departure offset at from. var newFromId = DirectedHelper.UpdateDepartureOffset(location.From, departureOffsetFrom); if (location.From != newFromId) { tour.Replace(location.From, newFromId); } var newToId = DirectedHelper.UpdateArrivalOffset(location.To, arrivalOffsetTo); if (location.To != newToId) { tour.Replace(location.To, newToId); } return cost; } } return Weight.Zero; // insert failed, probably costs are too high (above given max parameter). }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1 containing v_3. /// </summary> /// <returns></returns> public bool Try3OptMoves(TSProblem problem, float[][] weights, Tour tour, int v1, int v2, int v3, float[] weightsV3, int v4, float weightV1V2PlusV3V4, float weightV1V4, out float delta) { var v2ArrivalId = DirectedHelper.ExtractArrivalId(v2); // TODO: optimize this, extract all v2 info in one go. var v2Id = DirectedHelper.ExtractId(v2); var betweenV4V1Enumerator = tour.Between(v4, v1).GetEnumerator(); if (betweenV4V1Enumerator.MoveNext()) { var v5 = betweenV4V1Enumerator.Current; if (v5 != v1) { while (betweenV4V1Enumerator.MoveNext()) { var v6 = betweenV4V1Enumerator.Current; //var weightV3V6 = weightsV3[v6]; var v6Id = DirectedHelper.ExtractId(v6); // TODO: optimize this, extract all v6 info in one go. var v6ArrivalId = DirectedHelper.ExtractArrivalId(v6); var v5DepartureId = DirectedHelper.ExtractDepartureId(v5); var weightV3V6 = weightsV3[v6ArrivalId]; //var weightV5V2 = weights[v5][v2]; var weightV5V2 = weights[v5DepartureId][v2ArrivalId]; //var weightV5V6 = weights[v5][v6]; var weightV5V6 = weights[v5DepartureId][v6ArrivalId]; if (v6Id == problem.First && !problem.Last.HasValue) { // set to zero if not closed. weightV3V6 = 0; weightV5V6 = 0; } if (v2Id == problem.First && !problem.Last.HasValue) { // set to zero if not closed. weightV5V2 = 0; } // calculate the total weight of the 'new' arcs. var weightNew = weightV1V4 + weightV3V6 + weightV5V2; // calculate the total weights. var weight = weightV1V2PlusV3V4 + weightV5V6; if (weight - weightNew > _epsilon) { // actually do replace the vertices. var countBefore = tour.Count; delta = weightNew - weight; tour.ReplaceEdgeFrom(v1, v4); tour.ReplaceEdgeFrom(v3, v6); tour.ReplaceEdgeFrom(v5, v2); //int count_after = route.Count; // set bits. this.Set(problem, v3, false); this.Set(problem, v5, false); return(true); // move succeeded. } v5 = v6; } } } delta = 0; return(false); }
/// <summary> /// Applies this operator. /// </summary> public bool Apply(STSProblem problem, STSPObjective objective, Tour solution, out STSPFitness delta) { var before = objective.Calculate(problem, solution); var weights = problem.Weights; delta = objective.Zero; // select random new customers to insert. var toInsert = new List <int>(); if (solution.Count < problem.Weights.Length && _toInsert > 0) { var i = _toInsert; while (solution.Count > 1 && i > 0) { i--; var current = RandomGeneratorExtensions.GetRandom().Generate(problem.Weights.Length / 2); var directedId = solution.GetDirectedId(current); if (directedId == Constants.NOT_SET && !toInsert.Contains(current)) { toInsert.Add(current); } } } // select existing customers, to reinsert. if (_toRemove > 0) { var i = _toRemove; while (solution.Count > 1 && i > 0) { i--; var index = RandomGeneratorExtensions.GetRandom().Generate(solution.Count); var directedId = solution.GetCustomerAt(index); if (directedId != Constants.NOT_SET) { var current = DirectedHelper.ExtractId(directedId); if (directedId != solution.First && directedId != solution.Last && solution.Remove(directedId) && !toInsert.Contains(current)) { toInsert.Add(current); } } } } // shuffle the customers to insert. toInsert.Shuffle(); // insert all customers without exceeding max. var fitness = objective.Calculate(problem, solution); foreach (var current in toInsert) { var cost = solution.InsertCheapestDirected(problem.Weights, problem.TurnPenalties, current, problem.Max - fitness.Weight); if (cost > 0) { fitness.Weight += cost; fitness.Customers++; } } var after = objective.Calculate(problem, solution); delta = objective.Subtract(problem, after, before); return(objective.CompareTo(problem, before, after) > 0); }
/// <summary> /// Returns true if there was an improvement, false otherwise. /// </summary> /// <returns></returns> public bool MoveViolatedForward(TSPTWProblem problem, TObjective objective, Tour solution, out float delta) { if (_validFlags == null) { _validFlags = new bool[problem.Times.Length / 2]; } float time, waitTime, violatedTime; int violated; var fitness = objective.Calculate(problem, solution, out violated, out violatedTime, out waitTime, out time, ref _validFlags); // if no violated customer found return false. if (violated == 0) { delta = 0; return(false); } // loop over all customers. var enumerator = solution.GetEnumerator(); var position = 0; while (enumerator.MoveNext()) { if (position == 0) { // don't move the first customer. position++; continue; } // get the id of the current customer. var current = enumerator.Current; var id = DirectedHelper.ExtractId(current); if (problem.Last == id) { // don't place a fixed last customer. position++; continue; } // is this customer violated. if (_validFlags[id]) { // no it's not, move on. position++; continue; } // move over all customers before the current position. var enumerator2 = solution.GetEnumerator(); var position2 = 0; while (enumerator2.MoveNext()) { if (position2 <= position) { // only consider placement after. position2++; continue; } var current2 = enumerator2.Current; var id2 = DirectedHelper.ExtractId(current2); if (problem.Last == id2 && problem.Last != problem.First) { // don't place a fixed last customer. position2++; continue; } // test and check fitness. // TODO: do best-placement? check best turn at 'current'. var shiftedTour = solution.GetShiftedAfter(current, current2); // generates a tour as if current was placed right after current2. var newFitness = objective.Calculate(problem, shiftedTour); if (newFitness < fitness) { // there is improvement! delta = fitness - newFitness; solution.ShiftAfter(current, current2); return(true); } position2++; } position++; } delta = 0; return(false); }