c++memorysegmentation-faultant-colony

What is happening with vector array here?


I'm solving the Traveling Salesman Problem via an ACO implementation in C++. However, I found out that the program I've built so far gives a segmentation fault. (Note: I've limited the algorithm to only do one iteration of the colony for debugging purposes).

First off, I have a total of 52 cities taken from a file, and I distribute the ants so that every city has the same number of ants starting from it.

To store the distances between every pair of cities, I'm using a vector of vectors of doubles called Map (a square matrix). However, half-way during the execution it looks like these vectors are deleted. In this instance, it happens when calculating the path for the ant number 55. I've added a section of code just to highlight exactly where it crashes:

//DEBUGGING SECTION
                cout << "Size Roulette: " << Roulette.size() << endl;
                cout << "Size Remain: " << RemainingCities.size() << endl;
                cout << "Size Map: " << Map.size() << " x " << Map[0].size() << endl;

                int k = 0;
                cout << "Test: Map access: " << endl;
                for(int i = 0; i < Map.size(); ++i) //  HERE IT CRASHES AT ANT NUMBER 55
                    cout << Map[0][i] << " ";
                cout << endl;

                cout << "Test: Operation: " << Map[Colony[ant_i][city_i-1]][RemainingCities[k]] << endl;

                Roulette[k] = pow((MAX_DIST - Map[Colony[ant_i][city_i-1]][RemainingCities[k]]), heur_coef) + pow((pheromones[Colony[ant_i][city_i-1]][RemainingCities[k]]), pher_coef);

//END OF DEBUGGING SECTION

There, the function Map[0].size() normally returns 52 (just like Map.size(), as it's supposed to be a square matrix), but at the crashing iteration it returns what looks like a memory address, and the moment I try to access any element, a segmentation fault occurs.

I have checked that the memory access is always correct, and I can access any other variable without issue except Map until that 55th ant. I've tried different seeds for the roulette method, but it always crashes at the same place.

I have also varied the number of ants of the colony. If it's just one ant per city, the program executes without issue, but for any higher amount the program always crashes at the 55th ant.

You can download the full cpp file and the reading .tsp file from github:

https://github.com/yitosmash/ACO

In any case, I'll leave the full function here:

void ACO(const vector<City>& cities, const vector<vector<double>>& Map, int max_it, int num_ants, double decay, double heur_coef, double pher_coef, double pher_coef_elit)
{

        srand(30);

    //Initialise colony of ants (each ant is a vector of city indices)
    vector<vector<int>> Colony(num_ants, vector<int>(cities.size(), 0));

    //Initialise pheromone matrix
    vector<vector<double>> pheromones(cities.size(), vector<double>(cities.size(), 0));
    //Initialise costs vector(for etilist expansion)
    vector<double> costs(cities.size(), 0);

    //Auxiliar vector of indices
    vector<int> cityIndices(cities.size());
    for (int i = 0; i < cities.size(); ++i)
        cityIndices[i] = i;

    //Longest distance from Map, used for heuristic values.
    vector<double> longests(cities.size(), 0);
    for(int i = 0; i < cities.size(); ++i)
        longests[i] = *(max_element(Map[i].begin(), Map[i].end()));

    const double MAX_DIST = *(max_element(longests.begin(), longests.end()));
    longests.clear();


    int i=0;
    while(i<max_it)
    {
        for(int ant_i = 0; ant_i < num_ants; ++ant_i)
        {
            cout << "Ant: " << ant_i << endl;
            //City for ant_i to start at; each ant is assigned a determined starting city
            int starting_city = (int) ((float)ant_i/num_ants*cities.size());
            //cout << starting_city << endl;
            Colony[ant_i][0] = starting_city;

            //Get a vector with the cities left to visit
            vector<int> RemainingCities = cityIndices;

            //Remove starting city from remaining cities
            RemainingCities.erase(RemainingCities.begin() + starting_city);

            //Create path for ant_i
            for(int city_i = 1; city_i < Colony[ant_i].size(); ++city_i)
            {
                cout << "Calculating city number: " << city_i << endl;
                //Create roulette for next city selection
                vector<double> Roulette(RemainingCities.size(), 0);
                double total = 0;

                //DEBUGGING SECTION
                cout << "Size Roulette: " << Roulette.size() << endl;
                cout << "Size Remain: " << RemainingCities.size() << endl;
                cout << "Size Map: " << Map.size() << " x " << Map[0].size() << endl;

                int k = 0;
                cout << "Test: Map access: " << endl;
                for(int i = 0; i < Map.size(); ++i) //  HERE IT CRASHES AT ANT NUMBER 55
                    cout << Map[0][i] << " ";
                cout << endl;

                cout << "Test: Operation: " << Map[Colony[ant_i][city_i-1]][RemainingCities[k]] << endl;

                Roulette[k] = pow((MAX_DIST - Map[Colony[ant_i][city_i-1]][RemainingCities[k]]), heur_coef) + pow((pheromones[Colony[ant_i][city_i-1]][RemainingCities[k]]), pher_coef);

                //END OF DEBUGGING SECTION

                for(int j = 0; j < RemainingCities.size(); ++j)
                {
                    //Heuristic value is MAX_DIST - current edge.
                    Roulette[j] = pow((MAX_DIST - Map[Colony[ant_i][city_i-1]][RemainingCities[j]]), heur_coef) + pow((pheromones[Colony[ant_i][city_i-1]][RemainingCities[j]]), pher_coef);
                    total += Roulette[j];
                }
                cout << endl;
                //Transform roulette into stacked probabilities
                Roulette[0] = Roulette[0]/total;

                for(int j = 1; j < Roulette.size(); ++j)
                    Roulette[j] = Roulette[j-1] + Roulette[j] / total;

                //Select a city from Roulette
                int chosen = 0;
                double r = (double) rand()/RAND_MAX;
                while(Roulette[chosen] < r)
                    chosen++;

                //Add chosen city to
                Colony[ant_i][city_i] = RemainingCities[chosen];
                RemainingCities.erase(RemainingCities.begin() + chosen);
            }
            cout << endl;
            //Save cost of ant_i, for elitist expansion
            costs[ant_i] = pathCost(Colony[ant_i], Map);
        }
        i++;
    }

}

Solution

  • That part is very suspicious :

    for(int i = 0; i < Map.size(); ++i) //  HERE IT CRASHES AT ANT NUMBER 55
       cout << Map[0][i] << " ";
    

    because i is the size of the map but you use it as an index in a probable string / vector, so you probably go out of the string/vector with an undefined behavior

    probably you want

    for(int i = 0; i < Map.size(); ++i)
       cout << Map[i] << " ";
    

    or

    for(int i = 0; i < Map[0].size(); ++i)
       cout << Map[0][i] << " ";
    

    As I said in a remark at a moment RemainingCities[0] values -163172699 first in

     cout << "Test: Operation: " << Map.at(Colony.at(ant_i).at(city_i-1)).at(RemainingCities.at(k)) << endl;
    

    so is not a valid index in Map, but there is visible reason to have that looking at the code, so the reason is a probable write out of a vector destructing your memory elements.

    To detect where I replaced all the [...] by .at(...) and the first error I have is in ACO at the line

     costs.at(ant_i) = pathCost(Colony.at(ant_i), Map);
    

    where ant_i values 52 while costs has 52 entries and Colony 260, so the error concerns costs

    note that ant_i is set by the loop

     for(int ant_i = 0; ant_i < num_ants; ++ant_i)
    

    and in that case num_ants value 260 so much more than the size of costs which is defined as

     vector<double> costs(cities.size(), 0);
    

    but cost is just allocated and set but never read, so its goal is just to destruct the memory.

    If I remove the two lines concerning it I do not have anymore an error and the program ends normally, there is no exception in a .at(...) and valgrind detect no error too.