Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/generate_graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <unordered_map>

void generate_graph(Graph &graph, vector<Node *> &coords, int R);
void generate_graph_parallel(Graph &graph, vector<Node *> &coords, int R);
void generate_label_based_graph(Graph &graph, const vector<vector<double>> &coords);
void generate_random_edges(Graph &graph, int maxEdgesPerNode);
void connect_subgraphs(Graph &globalGraph, unordered_map<int, vector<Node *>> &PfMap);
Expand Down
1 change: 1 addition & 0 deletions include/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ int findMedoid_random(const vector<vector<double>> &coords);
int findMedoidInSubset(const vector<vector<double>> &coords);
vector<vector<double>> convert_to_double(const vector<vector<float>> &float_vector);
void initialize_graph(Graph &G, const vector<vector<double>> &coords);
void initialize_graph_parallel(Graph &G, const vector<vector<double>> &coords);
unordered_map<int, set<int>> computeFx(Graph &graph);

#endif
2 changes: 1 addition & 1 deletion src/filtered_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
85 changes: 85 additions & 0 deletions src/generate_graph.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* Contains helper functions in populating the graphs */

#include "../include/graph.hpp"
#include "../include/generate_graph.hpp"
#include <cstdlib>
Expand Down Expand Up @@ -50,6 +52,7 @@ void generate_graph(Graph &graph, vector<Node *> &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();
Expand All @@ -72,6 +75,88 @@ void generate_graph(Graph &graph, vector<Node *> &coords, int R)
}
}

#include <omp.h>

void generate_graph_parallel(Graph &graph, vector<Node *> &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<int>(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<size_t> 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<vector<double>> &coords)
{
Expand Down
4 changes: 2 additions & 2 deletions src/stitched_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions src/utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,44 @@ void initialize_graph(Graph &G, const vector<vector<double>> &coords)
}
}

void initialize_graph_parallel(Graph &G, const vector<vector<double>> &coords)
{
// create a local vector to hold nodes, reducing contention on the graph
vector<Node *> 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<int>(coords[i][0]);

// remaining elements are the coordinates
vector<double> pointCoords(coords[i].begin() + 1, coords[i].end());

// create a new Node
nodes[i] = new Node(static_cast<int>(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<int, set<int>> computeFx(Graph &G)
{
// find each node's label and store it in Fx
Expand Down
125 changes: 61 additions & 64 deletions tests/test_generate_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, vector<Node *>> PfMap;

// // create subgraph 0
// vector<Node *> 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<Node *> 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<Node *> 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<Node *> 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<Node *> 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<Node *> 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<int, vector<Node *>> 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();
}
Loading