Exemple #1
0
        /// <summary>
        /// Calculates the weight for a part of the tour including the turns at first and last position.
        /// </summary>
        /// <param name="part">The part to calculate for.</param>
        /// <param name="timeBefore">The time when arriving at the first customer, excluding the turn at that first customer.</param>
        public int TimeAndViolationsForPart(IEnumerable <int> part, float timeBefore, out float time, out float waitTime, out float violatedTime, ref bool[] validFlags)
        {
            var times         = this.Times;
            var windows       = this.Windows;
            var turnPenalties = this.TurnPenalties;

            time         = timeBefore;
            violatedTime = 0f;
            waitTime     = 0f;

            var violated     = 0;
            var previousFrom = int.MaxValue;

            foreach (var directedId in part)
            {
                // extract turns and stuff from directed id.
                int arrivalId, departureId, id, turn;
                DirectedHelper.ExtractAll(directedId, out arrivalId, out departureId, out id, out turn);

                // add the weight from the previous customer to the current one.
                var turnPenalty = 0f;
                if (previousFrom != int.MaxValue)
                { // there is a previous, add the travel time.
                    time = time + times[previousFrom][arrivalId];

                    // and keep the turn penalty.
                    turnPenalty += turnPenalties[turn];
                }
                else
                { // first customer.
                    // add turn penalty at first customer.
                    time += turnPenalties[DirectedHelper.ExtractTurn(directedId)];
                }

                // check the windows (before turn-penalties).
                var window = windows[id];
                validFlags[id] = true;
                if (window.Max < (time + waitTime))
                { // ok, unfeasible.
                    violatedTime  += (time + waitTime) - window.Max;
                    validFlags[id] = false;
                    violated++;
                }
                if (window.Min > (time + waitTime))
                { // wait here!
                    waitTime += (window.Min - (time + waitTime));
                }

                // add the turn penalty.
                time += turnPenalty;

                previousFrom = departureId;
            }

            time = time - timeBefore; // correct the time again, the time returned is the time of only this part.

            return(violated);
        }
Exemple #2
0
        /// <summary>
        /// Calculates the fitness value of the given solution.
        /// </summary>
        public sealed override float Calculate(TSProblem problem, Tour solution)
        {
            if (solution.Count == 1)
            {
                return(0);
            }

            var isLoop = solution.Last == solution.First;

            var weights      = problem.Weights;
            var weight       = 0f;
            var previousFrom = int.MaxValue;
            var firstTo      = int.MaxValue;
            var lastTurn     = 0f;

            foreach (var directedId in solution)
            {
                // extract turns and stuff from directed id.
                int arrivalId, departureId, id, turn;
                DirectedHelper.ExtractAll(directedId, out arrivalId, out departureId, out id, out turn);

                // add the weight from the previous customer to the current one.
                if (previousFrom != int.MaxValue)
                {
                    weight = weight + weights[previousFrom][arrivalId];
                }
                else
                {
                    firstTo = arrivalId;
                }

                // add turn penalty.
                if (isLoop || id != problem.First)
                {
                    lastTurn = problem.TurnPenalties[turn];
                    weight  += lastTurn;
                }

                previousFrom = departureId;
            }

            if (solution.Last == null)
            {
                weight -= lastTurn;
            }

            // add the weight between last and first.
            if (previousFrom != int.MaxValue &&
                isLoop)
            {
                weight = weight + weights[previousFrom][firstTo];
            }
            return(weight);
        }
Exemple #3
0
        /// <summary>
        /// Calculates the fitness value of the given solution.
        /// </summary>
        public sealed override STSPFitness Calculate(STSProblem problem, Tour solution)
        {
            // TODO: unittest this stuff!
            var fitness = new STSPFitness()
            {
                Customers = solution.Count,
                Weight    = 0
            };

            var weights      = problem.Weights;
            var weight       = 0f;
            var previousFrom = int.MaxValue;
            var firstTo      = int.MaxValue;

            foreach (var directedId in solution)
            {
                // extract turns and stuff from directed id.
                int arrivalId, departureId, id, turn;
                DirectedHelper.ExtractAll(directedId, out arrivalId, out departureId, out id, out turn);

                // add the weight from the previous customer to the current one.
                if (previousFrom != int.MaxValue)
                {
                    weight = weight + weights[previousFrom][arrivalId];
                }
                else
                {
                    firstTo = arrivalId;
                }

                // add turn penalty.
                weight += problem.TurnPenalties[turn];

                previousFrom = departureId;
            }

            // add the weight between last and first.
            if (previousFrom != int.MaxValue &&
                solution.First == solution.Last)
            {
                weight = weight + weights[previousFrom][firstTo];
            }
            fitness.Weight = weight;
            return(fitness);
        }
Exemple #4
0
        /// <summary>
        /// Calculates the fitness value of the given solution.
        /// </summary>
        public sealed override float Calculate(SequenceDirectedProblem problem, Tour solution)
        {
            var weights      = problem.Weights;
            var weight       = 0f;
            var previousFrom = int.MaxValue;
            var firstTo      = int.MaxValue;

            foreach (var directedId in solution)
            {
                // extract turns and stuff from directed id.
                int arrivalId, departureId, id, turn;
                DirectedHelper.ExtractAll(directedId, out arrivalId, out departureId, out id, out turn);

                // add the weight from the previous customer to the current one.
                if (previousFrom != int.MaxValue)
                {
                    weight = weight + weights[previousFrom][arrivalId];
                }
                else
                {
                    firstTo = arrivalId;
                }

                // add turn penalty.
                weight += problem.TurnPenalties[turn];

                previousFrom = departureId;
            }

            // add the weight between last and first.
            if (previousFrom != int.MaxValue &&
                solution.First == solution.Last)
            {
                weight = weight + weights[previousFrom][firstTo];
            }
            return(weight);
        }
        /// <summary>
        /// Applies this operator.
        /// </summary>
        public bool Apply(SequenceDirectedProblem problem, SequenceDirectedObjective objective, Tour solution, out float delta)
        {
            var weights = problem.Weights;

            delta = objective.Zero;

            var fitnessBefore = objective.Calculate(problem, solution);

            var enumerator = solution.GetEnumerator();

            while (enumerator.MoveNext())
            {
                var previous            = enumerator.Current;
                var previousDepartureId = DirectedHelper.ExtractDepartureId(previous);
                var current             = solution.GetNeigbour(previous);
                if (current == Constants.NOT_SET)
                { // last of open.
                    continue;
                }
                var next          = solution.GetNeigbour(current);
                var nextArrivalId = Constants.NOT_SET;
                if (next != Constants.NOT_SET)
                {
                    nextArrivalId = DirectedHelper.ExtractArrivalId(next);
                }
                int currentTurn, currentId, currentArrivalId, currentDepartureId;
                DirectedHelper.ExtractAll(current, out currentArrivalId, out currentDepartureId, out currentId, out currentTurn);

                var weightBefore  = weights[previousDepartureId][currentArrivalId];
                var weightAfter   = float.MaxValue;
                var newDirectedId = Constants.NOT_SET;
                if (next == Constants.NOT_SET)
                { // there is no next, only one option here:
                    // 0 or 1: arrival id offset = 0 OR
                    // 2 or 3: arrival id offset = 1
                    if (currentArrivalId % 2 == 0)
                    { // check turn = 2.
                        weightAfter = weights[previousDepartureId][currentArrivalId + 1];
                        if (weightAfter < weightBefore)
                        { // do the switch.
                            newDirectedId = DirectedHelper.BuildDirectedId(currentId, 2);
                        }
                    }
                    else
                    { // check turn = 0
                        weightAfter = weights[previousDepartureId][currentArrivalId - 1];
                        if (weightAfter < weightBefore)
                        { // do the switch.
                            newDirectedId = DirectedHelper.BuildDirectedId(currentId, 0);
                        }
                    }
                }
                else
                { // three options left, excluding the current one of 4.
                    weightBefore += weights[currentDepartureId][nextArrivalId];
                    weightBefore += problem.TurnPenalties[currentTurn];

                    currentArrivalId   = currentArrivalId - (currentArrivalId % 2);
                    currentDepartureId = currentDepartureId - (currentDepartureId % 2);
                    for (var i = 0; i < 4; i++)
                    {
                        if (i == currentTurn)
                        {
                            continue;
                        }

                        int arrivalOffset, departureOffset;
                        DirectedHelper.ExtractOffset(i, out arrivalOffset, out departureOffset);
                        var newWeightAfter = weights[previousDepartureId][currentArrivalId + arrivalOffset]
                                             + weights[currentDepartureId + departureOffset][nextArrivalId]
                                             + problem.TurnPenalties[i];
                        if (newWeightAfter < weightAfter &&
                            newWeightAfter < weightBefore)
                        {
                            newDirectedId = DirectedHelper.BuildDirectedId(currentId, i);
                            weightAfter   = newWeightAfter;
                        }
                    }
                }

                // switch if a new directed if was found.
                if (newDirectedId != Constants.NOT_SET)
                {
                    solution.Replace(current, newDirectedId);
                }
            }

            var fitnessAfter = objective.Calculate(problem, solution);

            delta = objective.Subtract(problem, fitnessBefore, fitnessAfter);

            return(objective.CompareTo(problem, delta, objective.Zero) > 0);
        }
Exemple #6
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);
        }
Exemple #7
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>
        /// Gets the minimum weight from customer1 -> customer3 while inserting customer2 and the best direction and turns to use.
        /// </summary>
        public static float CalculateCheapestInsert(this float[][] weights, float[] penalties, int directedId1, int id2, int directedId3,
            bool includeTurn1, bool includeTurn3, out int departureOffset1, out int arrivalOffset3, out int turn2)
        {
            // extract existing data.
            int id1, turn1, arrivalId1, departureId1, arrivalOffset1, temp;
            DirectedHelper.ExtractAll(directedId1, out arrivalId1, out departureId1, out id1, out turn1);
            DirectedHelper.ExtractOffset(turn1, out arrivalOffset1, out temp);
            int id3, turn3, arrivalId3, departureId3, departureOffset3;
            DirectedHelper.ExtractAll(directedId3, out arrivalId3, out departureId3, out id3, out turn3);
            DirectedHelper.ExtractOffset(turn3, out temp, out departureOffset3);

            // calculate current weight.
            var weightBefore = weights[departureId1][arrivalId3];
            if (includeTurn1)
            {
                weightBefore += penalties[turn1];
            }
            if (includeTurn3)
            {
                weightBefore += penalties[turn3];
            }

            // evaluate all possibilities.
            var best = float.MaxValue;
            var base1 = id1 * 2;
            var base2 = id2 * 2;
            var base3 = id3 * 2;
            departureOffset1 = Constants.NOT_SET;
            arrivalOffset3 = Constants.NOT_SET;
            turn2 = Constants.NOT_SET;
            for (var do1 = 0; do1 < 2; do1++)
            {
                var d1 = base1 + do1;
                var turn1Weight = 0f;
                if (includeTurn1)
                {
                    turn1 = DirectedHelper.BuildTurn(arrivalOffset1, do1);
                    turn1Weight = penalties[turn1];
                }
                for (var ao3 = 0; ao3 < 2; ao3++)
                {
                    var a3 = base3 + ao3;
                    var turn3Weight = 0f;
                    if (includeTurn3)
                    {
                        turn3 = DirectedHelper.BuildTurn(departureOffset3, ao3);
                        turn3Weight = penalties[turn3];
                    }
                    for (var t = 0; t < 4; t++)
                    {
                        int ao2, do2;
                        DirectedHelper.ExtractOffset(t, out ao2, out do2);

                        var weight = weights[d1][base2 + ao2] +
                            weights[base2 + do2][a3] +
                            penalties[t] + turn1Weight +
                            turn3Weight;

                        if (weight < best)
                        {
                            best = weight;
                            departureOffset1 = do1;
                            arrivalOffset3 = ao3;
                            turn2 = t;
                        }
                    }
                }
            }
            return best - weightBefore;
        }
Exemple #9
0
        /// <summary>
        /// Calculates total time and violations if the given id was moved after 'before'. Assumes the tour is closed.
        /// </summary>
        public int TimeAndViolations(IEnumerable <int> tour, out float time, out float waitTime, out float violatedTime, ref bool[] validFlags)
        {
            var times         = this.Times;
            var windows       = this.Windows;
            var turnPenalties = this.TurnPenalties;

            time         = 0f;
            violatedTime = 0f;
            waitTime     = 0f;

            var violated     = 0;
            var previousFrom = int.MaxValue;
            var firstTo      = int.MaxValue;
            var first        = Constants.NOT_SET;

            foreach (var directedId in tour)
            {
                if (first == Constants.NOT_SET)
                {
                    first = directedId;
                }

                // extract turns and stuff from directed id.
                int arrivalId, departureId, id, turn;
                DirectedHelper.ExtractAll(directedId, out arrivalId, out departureId, out id, out turn);

                // add the weight from the previous customer to the current one.
                var turnPenalty = 0f;
                if (previousFrom != int.MaxValue)
                { // there is a previous, add the travel time.
                    time = time + times[previousFrom][arrivalId];

                    // and keep the turn penalty.
                    turnPenalty += turnPenalties[turn];
                }
                else
                { // first customer.
                    firstTo = arrivalId;
                }

                // check the windows (before turn-penalties).
                var window = windows[id];
                validFlags[id] = true;
                if (window.Max < (time + waitTime))
                { // ok, unfeasible.
                    violatedTime  += (time + waitTime) - window.Max;
                    validFlags[id] = false;
                    violated++;
                }
                if (window.Min > (time + waitTime))
                { // wait here!
                    waitTime += (window.Min - (time + waitTime));
                }

                // add the turn penalty.
                time += turnPenalty;

                previousFrom = departureId;
            }

            // add the weight between last and first.
            if (previousFrom != int.MaxValue)
            {
                time = time + times[previousFrom][firstTo];

                // add turn penalty at first customer.
                time += turnPenalties[DirectedHelper.ExtractTurn(first)];
            }

            return(violated);
        }
        /// <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 weights = problem.Weights;

            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();
            }

            while (_pool.MoveNext())
            {
                var previous            = solution.GetDirectedId(_pool.Current);
                var previousDepartureId = DirectedHelper.ExtractDepartureId(previous);
                var current             = solution.GetNeigbour(previous);
                if (current == Constants.NOT_SET)
                { // last of open.
                    continue;
                }
                var next          = solution.GetNeigbour(current);
                var nextArrivalId = Constants.NOT_SET;
                if (next != Constants.NOT_SET)
                {
                    nextArrivalId = DirectedHelper.ExtractArrivalId(next);
                }
                int currentTurn, currentId, currentArrivalId, currentDepartureId;
                DirectedHelper.ExtractAll(current, out currentArrivalId, out currentDepartureId, out currentId, out currentTurn);

                var weightBefore  = weights[previousDepartureId][currentArrivalId];
                var weightAfter   = float.MaxValue;
                var newDirectedId = Constants.NOT_SET;
                if (next == Constants.NOT_SET)
                { // there is no next, only one option here:
                    // 0 or 1: arrival id offset = 0 OR
                    // 2 or 3: arrival id offset = 1
                    if (currentArrivalId % 2 == 0)
                    { // check turn = 2.
                        weightAfter = weights[previousDepartureId][currentArrivalId + 1];
                        if (weightAfter < weightBefore)
                        { // do the switch.
                            newDirectedId = DirectedHelper.BuildDirectedId(currentId, 2);
                        }
                    }
                    else
                    { // check turn = 0
                        weightAfter = weights[previousDepartureId][currentArrivalId - 1];
                        if (weightAfter < weightBefore)
                        { // do the switch.
                            newDirectedId = DirectedHelper.BuildDirectedId(currentId, 0);
                        }
                    }
                }
                else
                { // three options left, excluding the current one of 4.
                    weightBefore += weights[currentDepartureId][nextArrivalId];
                    weightBefore += problem.TurnPenalties[currentTurn];

                    currentArrivalId   = currentArrivalId - (currentArrivalId % 2);
                    currentDepartureId = currentDepartureId - (currentDepartureId % 2);
                    for (var i = 0; i < 4; i++)
                    {
                        if (i == currentTurn)
                        {
                            continue;
                        }

                        int arrivalOffset, departureOffset;
                        DirectedHelper.ExtractOffset(i, out arrivalOffset, out departureOffset);
                        var newWeightAfter = weights[previousDepartureId][currentArrivalId + arrivalOffset]
                                             + weights[currentDepartureId + departureOffset][nextArrivalId]
                                             + problem.TurnPenalties[i];
                        if (newWeightAfter < weightAfter &&
                            newWeightAfter < weightBefore)
                        {
                            newDirectedId = DirectedHelper.BuildDirectedId(currentId, i);
                            weightAfter   = newWeightAfter;
                        }
                    }
                }

                // switch if a new directed if was found.
                if (newDirectedId != Constants.NOT_SET)
                {
                    delta = weightAfter - weightBefore; // negative when improvement.

                    solution.Replace(current, newDirectedId);
                }
            }

            return(delta < 0);
        }