0

I am trying to use the Boost implementation of Boykov-Kolmogorov max-flow algorithm.

Here is how I do it:

typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS,
boost::no_property,
boost::property<boost::edge_index_t, std::size_t> > GraphType;

typedef boost::graph_traits<GraphType>::vertex_descriptor VertexDescriptor;
typedef boost::graph_traits<GraphType>::edge_descriptor EdgeDescriptor;
typedef boost::graph_traits<GraphType>::vertices_size_type VertexIndex;
typedef boost::graph_traits<GraphType>::edges_size_type EdgeIndex;

int numberOfVertices; //The number of vertices in my graph
std::vector<std::set<int>> neighbours(numberOfVertices);

// I fill the neighbours vector with information about neighbouring vertices

int sourceId = numberOfVertices;
int sinkId = sourceId + 1;

std::size_t size = 0;
for (int i = 0; i < neighbours.size(); i++) {
    size += neighbours[i].size();
}


std::vector<float> sourceWeights(numberOfVertices);
std::vector<float> sinkWeights(numberOfVertices);

for (int i = 0; i < numberOfVertices; i++) {
    sourceWeights[i] = 1.0f;
    sinkWeights[i] = 1.0f;
}

std::vector<int> groups(numberOfVertices, 0);
std::vector<float> capacity(3 * numberOfVertices + size, 0.0f);
std::vector<EdgeDescriptor> reverseEdges(3 * numberOfVertices + size);

GraphType graph;

for (int i = 0; i < numberOfVertices; i++) {
    boost::add_vertex(graph);
}

std::vector<std::pair<int, int>> edges((size - numberOfVertices) / 2);
int countEdges = 0;
for (int i = 0; i < neighbours.size(); i++) {
    for (int c : neighbours[i]) {
        if (i < c) {
            edges[countEdges] = (std::pair<int, int>(i, c));
            countEdges++;
        }
    }
}
//I create the edges as std::pair<int,int>
//in order to add them to the graph after with the corresponding weights

for (int i = 0; i < edges.size(); i++) {
    float weight = 1.0f;

    int nextEdgeId = 2 * i;

    EdgeDescriptor edge;
    bool inserted;
    boost::tie(edge, inserted) = boost::add_edge(boost::vertex(edges[i].first, graph), boost::vertex(edges[i].second, graph), nextEdgeId, graph);
    if (!inserted)
    {
        std::cerr << "Not inserted!" << std::endl;
    }
    EdgeDescriptor reverseEdge = boost::add_edge(boost::vertex(edges[i].second, graph), boost::vertex(edges[i].first, graph), nextEdgeId + 1, graph).first;

    reverseEdges[nextEdgeId] = reverseEdge;
    reverseEdges[nextEdgeId + 1] = edge;
    capacity[nextEdgeId] = weight;
    capacity[nextEdgeId + 1] = weight;
}

VertexDescriptor sourceVertex = boost::vertex(sourceId, graph);
VertexDescriptor sinkVertex = boost::vertex(sinkId, graph);

for (int i = 0; i < numberOfVertices; i++) {
    int nextEdgeId = 2 * edges.size() + 4 * i;

    EdgeDescriptor edge;
    bool inserted;
    boost::tie(edge, inserted) = boost::add_edge(boost::vertex(i, graph), sourceVertex, nextEdgeId, graph);
    if (!inserted)
    {
        std::cerr << "Not inserted!" << std::endl;
    }
    EdgeDescriptor reverseEdge = boost::add_edge(sourceVertex, boost::vertex(i, graph), nextEdgeId + 1, graph).first;

    reverseEdges[nextEdgeId] = reverseEdge;
    reverseEdges[nextEdgeId + 1] = edge;
    capacity[nextEdgeId] = sourceWeights[i];
    capacity[nextEdgeId + 1] = sourceWeights[i];


    int nextEdgeId2 = nextEdgeId + 2;

    EdgeDescriptor edge2;
    bool inserted2;
    boost::tie(edge2, inserted2) = boost::add_edge(boost::vertex(i, graph), sinkVertex, nextEdgeId2, graph);
    if (!inserted2)
    {
        std::cerr << "Not inserted!" << std::endl;
    }
    EdgeDescriptor reverseEdge2 = boost::add_edge(sinkVertex, boost::vertex(i, graph), nextEdgeId2 + 1, graph).first;

    reverseEdges[nextEdgeId2] = reverseEdge2;
    reverseEdges[nextEdgeId2 + 1] = edge2;
    capacity[nextEdgeId2] = sinkWeights[i];
    capacity[nextEdgeId2 + 1] = sinkWeights[i];

}
std::vector<float> residual_capacity(boost::num_edges(graph), 0.0f);

//I launch the algorithm by using all the vectors I defined previously

boost::boykov_kolmogorov_max_flow(graph,
    boost::make_iterator_property_map(&capacity[0], boost::get(boost::edge_index, graph)),
    boost::make_iterator_property_map(&residual_capacity[0], boost::get(boost::edge_index, graph)),
    boost::make_iterator_property_map(&reverseEdges[0], boost::get(boost::edge_index, graph)),
    boost::make_iterator_property_map(&groups[0], boost::get(boost::vertex_index, graph)),
    boost::get(boost::vertex_index, graph),
    sourceVertex,
    sinkVertex);

I am using this code on several images (around 100). There's a lot of preprocessing on the images to extract clusters that will act as the vertices of my graph and the connectivity information between these clusters.

The code runs fine on most of the images. When I launch my program, it runs on 10 images perfectly but then, on the 11th image, for no apparent reason, it crashes with the message

Unhandled exception at 0x00007FF9B52DE6FC (ntdll.dll) in Program.exe: 0xC0000374: A heap has been corrupted (parameters: 0x00007FF9B53322B0).

It also crashes directly if I only run it on the 11th image.

The call stack is really long (thanks Boost for all those templates) so I won't post it here but the application is failing when calling boost::boykov_kolmogorov_max_flow. If I remove the line where I call this function, the program runs perfectly on all images (even the 11th one).

I am running this program in Visual Studio 2013 on a Windows 10 64-bit machine with 64Gb RAM.

So my question is, what could be the cause of this heap corruption error that only shows up on certain images (which, in this case, only means different number of vertices and neighbours information since I set the weights at 1.0 everywhere) ?

Sunreef
  • 4,452
  • 21
  • 33
  • This line makes me nervous: `std::vector> edges((size - numberOfVertices) / 2);` – Sohail Jun 10 '16 at 12:47
  • @cheez Nervous how ? That the allocated size isn't right ? I checked with the `countEdges` variable and this is the right number of edges added to the graph. This strange formula is because when I compute my neighbours vector, I allow vertices to be their own neighbours (hence the `- numberOfVertices`) and I don't check if the connection already exists (hence the `/2`) – Sunreef Jun 10 '16 at 12:49
  • 2
    The problem is definitely that one of your size calculations is wrong, but I don't know enough to know which. – Sohail Jun 10 '16 at 12:51
  • A good desperate measure is to go through the program and make all the memory allocations twice as large as they should be. If the behaviour changes in any way, then you know you need to think harder about the sizes. If not, you know that you *probably* don't need to worry about them. – nugae Jun 10 '16 at 13:18
  • 2
    Another good idea is to share a minimal failing sample (notable the missing data here). So we can actually try it and help you – sehe Jun 10 '16 at 13:23
  • I don't see any place where `numverOfVertices` is initialized. That makes all of the subsequent code use an indeterminate value – sehe Jun 10 '16 at 13:25
  • @cheez You, sir, are my hero. After reading your comment, I went and checked all the sizes of my vectors. I wasn't wrong in my size calculations for edges. But the boykov_kolmogorov funciton expects a `groups` vector of size `numberOfVertices + 2` to account for the source and sink vertices (which seems strange to me since they already have a defined label) and not just a `numberOfVertices` size. With this change, it runs well on all images. The mystery now is: how was it able to run before on most of the images ? – Sunreef Jun 10 '16 at 13:26
  • @sehe I initialize the variable in my code. I just didn't show it here since it is quite long and seemed unrelated to my error. – Sunreef Jun 10 '16 at 13:27
  • You **did** code that yourself (`size_t sourceId = numberOfVertices; int sinkId = sourceId + 1;` is pretty prominent). On "how was it able to run": [welcome to Undefined Behaviour](https://en.wikipedia.org/wiki/Undefined_behavior) – sehe Jun 10 '16 at 13:28
  • @Sunreef it is unrelated to the error, but it's very related to reproducing it :) See http://sscce.org/ and http://stackoverflow.com/help/mcve – sehe Jun 10 '16 at 13:28
  • @Sunreef Glad to hear you got it figured out. I wouldn't think too hard about why it worked though since you were causing the algorithm to perform undefined behaviour. If you're using Visual Studio, you should enable iterator debugging: _HAS_ITERATOR_DEBUGGING. This might have helped you sooner. VS in particular has a bunch of nice stuff like this that can help you sooner. – Sohail Jun 10 '16 at 14:51

0 Answers0