diff --git a/include/generate_graph.hpp b/include/generate_graph.hpp index 2c851c2..46b69f2 100644 --- a/include/generate_graph.hpp +++ b/include/generate_graph.hpp @@ -5,6 +5,7 @@ #include void generate_graph(Graph &graph, vector &coords, int R); +void generate_graph_parallel(Graph &graph, vector &coords, int R); void generate_label_based_graph(Graph &graph, const vector> &coords); void generate_random_edges(Graph &graph, int maxEdgesPerNode); void connect_subgraphs(Graph &globalGraph, unordered_map> &PfMap); diff --git a/include/utility.hpp b/include/utility.hpp index d436cef..1799363 100644 --- a/include/utility.hpp +++ b/include/utility.hpp @@ -17,6 +17,7 @@ int findMedoid_random(const vector> &coords); int findMedoidInSubset(const vector> &coords); vector> convert_to_double(const vector> &float_vector); void initialize_graph(Graph &G, const vector> &coords); +void initialize_graph_parallel(Graph &G, const vector> &coords); unordered_map> computeFx(Graph &graph); #endif \ No newline at end of file diff --git a/src/filtered_main.cpp b/src/filtered_main.cpp index 3d80ee9..50a7307 100644 --- a/src/filtered_main.cpp +++ b/src/filtered_main.cpp @@ -304,7 +304,7 @@ int main(int argc, char* argv[]) { filtered_vamana_duration = end - start; memoryUsed = memoryAfter - memoryBefore; cout << "FilteredVamana took " << filtered_vamana_duration.count() << " seconds." << endl; - cout << "Memory used by FilteredVamana: " << memoryUsed / 1024.0 << " MB\n" << endl; // convert KB to MB + cout << "Memory used by FilteredVamana: " << memoryUsed / 1024.0 << " MB.\n" << endl; // convert KB to MB } else { // find L from filename L = -1; // Default value in case L is not found diff --git a/src/generate_graph.cpp b/src/generate_graph.cpp index e75fbd9..d81cee7 100644 --- a/src/generate_graph.cpp +++ b/src/generate_graph.cpp @@ -1,3 +1,5 @@ +/* Contains helper functions in populating the graphs */ + #include "../include/graph.hpp" #include "../include/generate_graph.hpp" #include @@ -50,6 +52,7 @@ void generate_graph(Graph &graph, vector &coords, int R) } } + // randomly add edges until the node reaches adjusted_R edges or no more neighbors are available while (edgesAdded < adjusted_R && !potentialNeighbors.empty()) { auto it = potentialNeighbors.begin(); @@ -72,6 +75,88 @@ void generate_graph(Graph &graph, vector &coords, int R) } } +#include + +void generate_graph_parallel(Graph &graph, vector &coords, int R) +{ + srand(time(0)); + + size_t n = coords.size(); // number of points in the current graph + if (n == 0) + { + cerr << "Error [generate_graph]: No coordinates provided." << endl; + return; // avoid processing empty graphs + } + + // adjust R if necessary + int adjusted_R = min(static_cast(n) - 1, R); + + // Step 1: Parallel addition of nodes + #pragma omp parallel for schedule(static) + for (size_t i = 0; i < coords.size(); ++i) + { + #pragma omp critical + { + graph.addNode(coords[i]); + } + } + + // Step 2: Parallel edge creation + #pragma omp parallel for schedule(static) + for (size_t i = 0; i < coords.size(); ++i) + { + Node *current = graph.getNode(coords[i]->getId()); + if (!current) + { + #pragma omp critical + { + cout << "Node with id: " << coords[i]->getId() << " does not exist in the graph.\n"; + } + continue; + } + + int edgesAdded = 0; + set potentialNeighbors; + + // add other nodes as potential neighbors + for (size_t j = 0; j < n; ++j) + { + if (j != i) + { + potentialNeighbors.insert(coords[j]->getId()); + } + } + + // randomly add edges until the node reaches adjusted_R edges or no more neighbors are available + while (edgesAdded < adjusted_R && !potentialNeighbors.empty()) + { + auto it = potentialNeighbors.begin(); + advance(it, rand() % potentialNeighbors.size()); // select a random neighbor + + size_t randomNeighborId = *it; + + #pragma omp critical + { + if (!current->edgeExists(randomNeighborId)) + { + graph.addEdge(coords[i]->getId(), randomNeighborId); + edgesAdded++; + } + } + + potentialNeighbors.erase(it); // remove the selected neighbor to avoid cycles! + } + + if (edgesAdded < adjusted_R) + { + #pragma omp critical + { + printf("Warning: Node %ld could only form %d edges.\n", i, edgesAdded); + } + } + } +} + // function to randomly connect nodes to nodes with the same label void generate_label_based_graph(Graph &graph, const vector> &coords) { diff --git a/src/stitched_main.cpp b/src/stitched_main.cpp index 68047fd..6266e04 100644 --- a/src/stitched_main.cpp +++ b/src/stitched_main.cpp @@ -304,8 +304,8 @@ int main(int argc, char* argv[]) { memoryAfter = getMemoryUsage(); stitched_vamana_duration = end - start; memoryUsed = memoryAfter - memoryBefore; - cout << "StitchedVamana took " << stitched_vamana_duration.count() << " seconds.\n" << endl; - cout << "Memory used by StitchedVamana: " << memoryUsed / 1024.0 << " MB\n" << endl; // convert KB to MB + cout << "StitchedVamana took " << stitched_vamana_duration.count() << " seconds." << endl; + cout << "Memory used by StitchedVamana: " << memoryUsed / 1024.0 << " MB.\n" << endl; // convert KB to MB } else { // find L from filename L = -1; // Default value in case L is not found diff --git a/src/utility.cpp b/src/utility.cpp index d4201d0..cce37a2 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -314,6 +314,44 @@ void initialize_graph(Graph &G, const vector> &coords) } } +void initialize_graph_parallel(Graph &G, const vector> &coords) +{ + // create a local vector to hold nodes, reducing contention on the graph + vector nodes(coords.size()); + + #pragma omp parallel for + for (size_t i = 0; i < coords.size(); i++) + { + if (coords[i].empty()) + { + cerr << "Error: Empty coordinate vector at index " << i << endl; + continue; + } + + // first element is the label + int label = static_cast(coords[i][0]); + + // remaining elements are the coordinates + vector pointCoords(coords[i].begin() + 1, coords[i].end()); + + // create a new Node + nodes[i] = new Node(static_cast(i), pointCoords, {}, label); + } + + // add the nodes to the graph + #pragma omp parallel + for (size_t i = 0; i < nodes.size(); i++) + { + if (nodes[i] != nullptr) + { + #pragma omp critical + { + G.addNode(nodes[i]); + } + } + } +} + unordered_map> computeFx(Graph &G) { // find each node's label and store it in Fx diff --git a/tests/test_generate_graph.cpp b/tests/test_generate_graph.cpp index 452d6fa..eea6b4b 100644 --- a/tests/test_generate_graph.cpp +++ b/tests/test_generate_graph.cpp @@ -224,67 +224,64 @@ TEST_CASE("Test generate_random_edges basic functionality") graph.clear(); } -// TEST_CASE("Test connect_subgraphs basic functionality") -// { -// Graph globalGraph; -// unordered_map> PfMap; - -// // create subgraph 0 -// vector subgraph0; -// subgraph0.push_back(new Node(0, {0.0, 0.0}, {}, 0)); -// subgraph0.push_back(new Node(1, {1.0, 1.0}, {}, 0)); -// PfMap[0] = subgraph0; - -// // create subgraph 1 -// vector subgraph1; -// subgraph1.push_back(new Node(2, {2.0, 2.0}, {}, 1)); -// subgraph1.push_back(new Node(3, {3.0, 3.0}, {}, 1)); -// PfMap[1] = subgraph1; - -// // create subgraph 2 -// vector subgraph2; -// subgraph2.push_back(new Node(4, {4.0, 4.0}, {}, 2)); -// subgraph2.push_back(new Node(5, {5.0, 5.0}, {}, 2)); -// PfMap[2] = subgraph2; - -// // add all nodes to the global graph -// for (const auto &entry : PfMap) -// { -// for (Node *node : entry.second) -// { -// globalGraph.addNode(node); -// } -// } - -// // call connect_subgraphs -// connect_subgraphs(globalGraph, PfMap); - -// // check that subgraphs are connected -// for (size_t i = 0; i < PfMap.size() - 1; ++i) -// { -// const auto &nodes1 = PfMap[i]; -// const auto &nodes2 = PfMap[i + 1]; - -// bool isConnected = false; - -// for (Node *node1 : nodes1) -// { -// for (Node *node2 : nodes2) -// { -// if (node1->edgeExists(node2->getId()) || node2->edgeExists(node1->getId())) -// { -// isConnected = true; -// break; -// } -// } -// if (isConnected) -// { -// break; -// } -// } - -// REQUIRE(isConnected); // ensure subgraph i is connected to subgraph i+1 -// } - -// globalGraph.clear(); -// } +TEST_CASE("Test connect_subgraphs basic functionality") +{ + Graph G; + + // create subgraph 1 + vector subgraph1 = { + new Node(0, {1.0, 2.0, 3.0}, {}, 0), + new Node(1, {4.0, 5.0, 6.0}, {}, 0)}; + + // create subgraph 2 + vector subgraph2 = { + new Node(2, {7.0, 8.0, 9.0}, {}, 1), + new Node(3, {10.0, 11.0, 12.0}, {}, 1)}; + + // create subgraph 3 + vector subgraph3 = { + new Node(4, {13.0, 14.0, 15.0}, {}, 2), + new Node(5, {16.0, 17.0, 18.0}, {}, 2)}; + + + // add all nodes to the global graph + for (Node *node : subgraph1) { + G.addNode(node); + } + for (Node *node : subgraph2) { + G.addNode(node); + } + for (Node *node : subgraph3) { + G.addNode(node); + } + + // map subgraphs by their labels + unordered_map> PfMap = { + {0, subgraph1}, + {1, subgraph2}, + {2, subgraph3}}; + + connect_subgraphs(G, PfMap); + + // check that the global graph connects the subgraphs --> there should be at least two edges between different subgraphs + int crossEdges = 0; + for (auto &[label, nodes] : PfMap) + { + for (Node *node : nodes) + { + for (auto edge : node->getEdges()) + { + Node *neighbor = G.getNode(edge->getId()); + REQUIRE(neighbor != nullptr); // Ensure neighbor exists + if (neighbor->getLabel() != node->getLabel()) + { + ++crossEdges; + } + } + } + } + + REQUIRE(crossEdges >= 2); // at least two edges connect distinct subgraphs + + G.clear(); +}