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);
            }
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
 private void Set(TSProblem problem, int directedCustomer, bool value)
 {
     if (_dontLook)
     {
         var originalId = DirectedHelper.ExtractId(directedCustomer);
         _dontLookBits[originalId] = value;
     }
 }
예제 #4
0
        /// <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);
        }
예제 #5
0
 private bool Check(TSProblem problem, int directedCustomer)
 {
     if (_dontLook)
     {
         var originalId = DirectedHelper.ExtractId(directedCustomer);
         return(_dontLookBits[originalId]);
     }
     return(false);
 }
예제 #6
0
        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);
            }
        }
예제 #7
0
        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);
            }
        }
예제 #8
0
        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);
            }
        }
예제 #9
0
        /// <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);
        }
예제 #10
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]));
        }
예제 #11
0
        /// <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);
        }
예제 #12
0
        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]);
            }
        }
예제 #13
0
        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);
            }
        }
예제 #15
0
        /// <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);
        }
예제 #16
0
        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);
            }
        }
예제 #17
0
        /// <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);
        }
예제 #18
0
        /// <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).
        }
예제 #20
0
        /// <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);
        }
예제 #22
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);
        }