/** * Parallel BFS to find P, L, and LQ values of the given graph */ void bfs(Graph *graph, int v, int *levels, int *P, int *L, int **LQ, int *LQCounts, int *visited) { int numThreads = omp_get_max_threads(); // List of vertices to visit - appended to during the search int *toVisit = malloc(sizeof(int) * graph->numVertices); int back = 0; int *toVisitThreads = malloc(sizeof(int) * (numThreads * graph->numVertices)); int *threadOffsets = malloc(sizeof(int) * numThreads); P[v] = v; L[v] = 0; LQ[0] = malloc(sizeof(int)); LQ[0][0] = v; LQCounts[0] = 1; toVisit[0] = v; back = 1; // Direction optimising BFS vars double alpha = 15.0; double beta = 25.0; int useBottomUp = 0; int nf = 0; int localnf; #pragma omp parallel { int tid = omp_get_thread_num(); int *toVisitThread = &toVisitThreads[tid * graph->numVertices]; int threadOffset = 0; int level = 1; int prevLevel; int vert, adjEnd, u; int * adjVertices; while(back) { threadOffset = 0; if(!useBottomUp) { /* Using top down approach */ #pragma omp for schedule(static) reduction(+:localnf) for(int i = 0; i < back; i++) { vert = toVisit[i]; adjVertices = adjacentVertices(graph, vert); adjEnd = outDegree(graph, vert); // Go through each vertex adjacent to v for(int j = 0; j < adjEnd; j++) { u = adjVertices[j]; if(L[u] < 0) { L[u] = level; P[u] = vert; // Add the adjacent vertex to the threads buffer toVisitThread[threadOffset++] = u; localnf++; } } } } else { /* Using bottom up approach */ prevLevel = level - 1; #pragma omp for schedule(static) reduction(+:localnf) for(int i = 0; i < graph->numVertices; i++) { vert = i; if(L[vert] < 0) { adjVertices = adjacentVertices(graph, vert); adjEnd = outDegree(graph, vert); for(int j = 0; j < adjEnd; j++) { u = adjVertices[j]; if(L[u] == prevLevel) { L[vert] = level; P[vert] = u; toVisitThread[threadOffset++] = vert; localnf++; break; } } } } } threadOffsets[tid] = threadOffset; #pragma omp barrier #pragma omp single { nf += localnf; // Determine whether to switch BFS approaches if(useBottomUp) { double mf = (double) localnf * graph->avgOutDegree; double mu = (double) (graph->numVertices - nf) * graph->avgOutDegree; // Switch if heuristic is true, and if we haven't switched yet if(mf > (mu / alpha) && mu > 0) { useBottomUp = 1; } } else { if(nf < ((double) graph->numVertices / beta)) { useBottomUp = 0; } } int oldBack = back; back = 0; // Copy each threads buffer of vertices into main array for(int i = 0; i < numThreads; i++) { if(threadOffsets[i]) { int offset = i * graph->numVertices; for(int j = 0; j < threadOffsets[i]; j++) { toVisit[back++] = toVisitThreads[offset + j]; } } } // Store this level in LQ LQ[level] = (int *) malloc(sizeof(int) * back); for(int i = 0; i < back; i++) { LQ[level][i] = toVisit[i]; } LQCounts[level] = back; (*levels)++; } level++; #pragma omp barrier } } free(toVisit); free(toVisitThreads); free(threadOffsets); }
void findLowValues(Graph *graph, int root, int *P, int *L, int *Par, int *Low, int **LQ, int *LQCounts, int *Art) { int numThreads = omp_get_max_threads(); // List of vertices to visit - appended to during the search int *toVisit = malloc(sizeof(int) * graph->numVertices); int back = 0; int *toVisitThreads = malloc(sizeof(int) * (numThreads * graph->numVertices)); int *threadOffsets = malloc(sizeof(int) * numThreads); int *stack = (int *) malloc(sizeof(int) * (graph->numVertices * 2)); int *lows = (int *) malloc(sizeof(int) * numThreads); int stackBack = 0; int *visited = (int *) malloc(sizeof(int) * graph->numVertices); for(int i = 0; i < graph->numVertices; i++) { visited[i] = 0; } int levelSize = LQCounts[1]; visited[root] = 1; // Direction optimising BFS vars double alpha = 14.0; double beta = 24.0; int useBottomUp = 0; int nf = 0; int localnf; for(int l = 0; l < levelSize; l++) { int globalLow = graph->numVertices; int v = LQ[1][l]; if(Low[v] == -1) { toVisit[0] = v; back = 1; Par[v] = 0; stack[0] = v; stackBack = 1; #pragma omp parallel { int tid = omp_get_thread_num(); int *toVisitThread = &toVisitThreads[tid * graph->numVertices]; int threadOffset = 0; int prevLevel; int vert, adjEnd, u; int *adjVertices; int threadLow = graph->numVertices; while(back) { threadOffset = 0; if(!useBottomUp) { #pragma omp for schedule(static) reduction(+:localnf) for(int i = 0; i < back; i++) { vert = toVisit[i]; adjEnd = outDegree(graph, vert); adjVertices = adjacentVertices(graph, vert); for(int j = 0; j < adjEnd; j++) { u = adjVertices[j]; if(!visited[u] && Low[u] < 0) { visited[u] = 1; Par[u] = 0; toVisitThread[threadOffset++] = u; if(u < threadLow) { threadLow = u; } } } } } else { #pragma omp for schedule(static) reduction(+:localnf) for(int i = 0; i < graph->numVertices; i++) { vert = i; if(!visited[vert] && Low[vert] < 0) { adjEnd = outDegree(graph, vert); adjVertices = adjacentVertices(graph, vert); for(int j = 0; j < adjEnd; j++) { u = adjVertices[j]; if(visited[j]) { visited[vert] = 1; Par[vert] = 0; toVisitThread[threadOffset++] = vert; if(vert < threadLow) { threadLow = vert; } localnf++; break; } } } } } threadOffsets[tid] = threadOffset; lows[tid] = threadLow; #pragma omp barrier #pragma omp single { nf += localnf; if(useBottomUp) { double mf = (double) localnf * graph->avgOutDegree; double mu = (double) (graph->numVertices - nf) * graph->avgOutDegree; // Switch if heuristic is true, and if we haven't switched yet if(mf > (mu / alpha) && mu > 0) { useBottomUp = 1; } } else { if(nf < ((double) graph->numVertices / beta)) { useBottomUp = 0; } } back = 0; for(int i = 0; i < numThreads; i++) { if(lows[i] < globalLow) { globalLow = lows[i]; } if(threadOffsets[i]) { int offset = i * graph->numVertices; for(int j = 0; j < threadOffsets[i]; j++) { toVisit[back++] = toVisitThreads[offset + j]; } } } for(int i = 0; i < back; i++) { stack[i + stackBack] = toVisit[i]; } stackBack += back; } } #pragma omp for for(int i = 0; i < stackBack; i++) { int v = stack[i]; Low[v] = globalLow; visited[v] = 0; } } } } // Deal with root int end = outDegree(graph, root); int *adjVertices = adjacentVertices(graph, root); for(int i = 0; i < end; i++) { int currLow = Low[adjVertices[i]]; for(int j = i + 1; j < end; j++) { if(Low[adjVertices[j]] == currLow) { Low[root] = currLow; break; } } if(Low[root] >= 0) { break; } } if(Low[root] < 0) { Art[root] = 1; Low[root] = root; } free(visited); free(toVisit); free(toVisitThreads); free(stack); free(threadOffsets); free(lows); }
void findArticulationPoints(Graph *graph, int *P, int *L, int *Par, int *Low, int **LQ, int *LQCounts, int *Art, int levels) { #pragma omp parallel { // Each thread maintains it's own visited array int *visited = (int *) malloc(sizeof(int) * graph->numVertices); for(int i = 0; i < graph->numVertices; i++) { visited[i] = 0; } // Tracks the list of unique vertices encountered int *stack = (int *) malloc(sizeof(int) * graph->numVertices); int *queue = (int *) malloc(sizeof(int) * graph->numVertices); int *nextQueue = (int *) malloc(sizeof(int) * graph->numVertices); int maxVert = 0; int stackBack; int back; int nextBack; int curLow; int isCurrArt; for(int l = levels - 1; l > 1; l--) { int *currQueue = LQ[l]; int levelSize = LQCounts[l]; #pragma omp for schedule(guided) for(int v = 0; v < levelSize; v++) { int vert = currQueue[v]; if(Low[vert] > -1) { continue; } int vertLevel = l; int vertParent = P[vert]; queue[0] = vert; back = 1; stack[0] = vert; stackBack = 1; visited[vert] = 1; isCurrArt = 0; curLow = graph->numVertices; while(back) { nextBack = 0; for(int j = 0; j < back; j++) { int newVert = queue[j]; int isNewArt = Art[newVert]; int *newOuts = adjacentVertices(graph, newVert); int newOutDegree = outDegree(graph, newVert); for(int n = 0; n < newOutDegree; n++) { int newOut = newOuts[n]; if(visited[newOut] || newOut == vertParent) { continue; } else if(isNewArt && Low[newOut] > -1) { continue; } else if(L[newOut] < vertLevel) { goto next_out; } else { visited[newOut] = 1; nextQueue[nextBack++] = newOut; stack[stackBack++] = newOut; if(newOut < curLow) curLow = newOut; } } } int *tmp = nextQueue; nextQueue = queue; queue = tmp; back = nextBack; } Art[vertParent] = 1; isCurrArt = 1; next_out: if(isCurrArt) { for(int j = 0; j < stackBack; j++) { int sVert = stack[j]; visited[sVert] = 0; Par[sVert] = vert; Low[sVert] = curLow; } } else { for(int j = 0; j < stackBack; j++) { int sVert = stack[j]; visited[sVert] = 0; } } } } free(visited); free(stack); free(queue); free(nextQueue); } }
/** * Finds the articulation points of the given graph by inspecting the queues in LQ in reverse order, and * determining Par and Low values */ void findArticulationPoints2(Graph *graph, int *P, int *L, int *Par, int *Low, int **LQ, int *LQCounts, int *Art, int levels) { #pragma omp parallel { // Each thread maintains it's own visited array int *visited = (int *) malloc(sizeof(int) * graph->numVertices); for(int i = 0; i < graph->numVertices; i++) { visited[i] = 0; } // Tracks the list of unique vertices encountered int *Vu = (int *) malloc(sizeof(int) * graph->numVertices); int *queue = (int *) malloc(sizeof(int) * graph->numVertices); int *nextQueue = (int *) malloc(sizeof(int) * graph->numVertices); int *queueWhole = (int *) malloc(sizeof(int) * graph->numVertices); int maxVert = 0; int stackEnd; int queueEnd, queueEndWhole; int nextQueueEnd; int isCurrArt; // Lowest vertex identifier encountered int vidLow; // Inspect LQ in reverse order (LQm..1) for(int level = levels - 1; level >= 1; level--) { int levelSize = LQCounts[level]; #pragma omp for schedule(guided) for(int uIndex = 0; uIndex < levelSize; uIndex++) { int u = LQ[level][uIndex]; if(Low[u] == -1) { // Get the parent of u int v = P[u]; queue[0] = u; queueEnd = 1; Vu[0] = u; stackEnd = 1; visited[u] = 1; isCurrArt = 0; vidLow = u; int queueIndex = 0; while(queueIndex < queueEnd) { int x = queue[queueIndex++]; int isXArt = Art[x]; int *xAdj = adjacentVertices(graph, x); int xAdjEnd = outDegree(graph, x); for(int wIndex = 0; wIndex < xAdjEnd; wIndex++) { int w = xAdj[wIndex]; if(visited[w] || w == v) { // This thread has already visited, or it's an edge back to parent, ignore! continue; } else if(isXArt && Low[w] > -1) { // Already know it's an articulation point, ignore! continue; } else if(L[w] < L[u]) { // Ref. lines 11 & 12, Alg. 8 (BFS-LV) // Jump out of this BFS and set everything on the stack to unvisited goto nextOut; } else { // Can keep searching! // Ref. lines 14--18, Alg. 8 (BFS-LV) // Add to the next queue so we process it after everything in the current queue queue[queueEnd++] = w; Vu[stackEnd++] = w; visited[w] = 1; if(w < vidLow) { vidLow = w; } } } } // Ref. line 14 - 21, Alg. 8 (BFS-LV) Art[v] = 1; numBiconnectedComponents++; isCurrArt = 1; // For each of the unique vertices encountered for(int j = 0; j < stackEnd; j++) { int w = Vu[j]; Low[w] = vidLow; Par[w] = v; visited[w] = 0; } nextOut: // Label to get out of nested loop above if(!isCurrArt) { // We'll get here from the jump above when L[w] < L[u] - set everything on stack to unvisited for(int j = 0; j < stackEnd; j++) { int s = Vu[j]; visited[s] = 0; } } } } } free(visited); free(Vu); free(queue); free(nextQueue); } }
int main (int argc, char * argv[]) { printf("Blackbox tests...\n"); printf("Test 1: newGraph..."); //empty graph Graph g1a = mkTestGraph(0); destroyGraph(g1a); //single graph Graph g1b = mkTestGraph(1); destroyGraph(g1b); printf("Passed!\n"); printf("Test 1a: mkEdge..."); //zero cost edge Edge e1a = mkEdge(0, 1, 0); assert(e1a.v == 0); assert(e1a.w == 1); assert(e1a.weight == 0); Edge e1ar = mkEdge(1, 0, 0); assert(e1ar.v == 1); assert(e1ar.w == 0); assert(e1ar.weight == 0); //edge Edge e1b = mkEdge(1, 5, 10); assert(e1b.v == 1); assert(e1b.w == 5); assert(e1b.weight == 10); Edge e1br = mkEdge(5, 1, 10); assert(e1br.v == 5); assert(e1br.w == 1); assert(e1br.weight == 10); printf("Passed!\n"); printf("Test 2: insertE..."); //double graph Graph g2 = mkTestGraph(2); Edge e2 = mkEdge(0, 1, 12); insertE(g2, e2); assert(numE(g2) == 1); destroyGraph(g2); printf("Passed!\n"); printf("Test 3: isAdjacent..."); //double graph Graph g3a = mkTestGraph(2); Edge e3a = {0, 1, 12}; insertE(g3a, e3a); assert(numV(g3a) == 2); assert(numE(g3a) == 1); assert(isAdjacent(g3a, 0, 1) == 1); assert(isAdjacent(g3a, 1, 0) == 1); destroyGraph(g3a); //graph Graph g3b = mkTestGraph(3); assert(isAdjacent(g3b, 0, 1) == 1); assert(isAdjacent(g3b, 0, 2) == 1); assert(isAdjacent(g3b, 0, 3) == 1); assert(isAdjacent(g3b, 0, 4) == 1); assert(isAdjacent(g3b, 1, 2) == 1); assert(isAdjacent(g3b, 2, 3) == 1); assert(isAdjacent(g3b, 3, 4) == 1); destroyGraph(g3b); printf("Passed!\n"); printf("Test 4: adjacentVertices..."); Graph g4a = mkTestGraph(2); Edge e4a = {0, 1, 12}; insertE(g4a, e4a); Vertex adj4a[2]; //allocate space for max number of vertices assert(adjacentVertices(g4a, 0, adj4a) == 1); assert(adj4a[0] >= 0); assert(adjacentVertices(g4a, 1, adj4a) == 1); assert(adj4a[0] >= 0); destroyGraph(g4a); printf("Passed!\n"); printf("Test 5: incidentEdges..."); Graph g5 = mkTestGraph(2); Edge e5 = {0, 1, 12}; insertE(g5, e5); Edge edges5[1]; //allocate space for max num of edges assert(incidentEdges(g5, 0, edges5) == 1); int v5 = edges5[0].v; int w5 = edges5[0].w; assert( (v5 == 0 && w5 == 1) || (v5 == 1 && w5 == 0) ); assert(edges5[0].weight == 12); assert(incidentEdges(g5, 1, edges5) == 1); v5 = edges5[0].v; w5 = edges5[0].w; assert( (v5 == 0 && w5 == 1) || (v5 == 1 && w5 == 0) ); assert(edges5[0].weight == 12); destroyGraph(g5); printf("Passed!\n"); printf("Test 6: edges..."); Graph g6 = mkTestGraph(2); Edge e6 = {0, 1, 12}; insertE(g6, e6); Edge es6[1]; //allocate space for max num of edges assert(edges(es6, 1, g6) == 1); int v6 = es6[0].v; int w6 = es6[0].w; assert( (v6 == 0 && w6 == 1) || (v6 == 1 && w6 == 0) ); assert(es6[0].weight == 12); destroyGraph(g6); printf("Passed!\n"); printf("All Test Passed! You are a Beast!\n"); return EXIT_SUCCESS; }