Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching Adjacency Matrix for faster edge lookup #344

Merged
merged 11 commits into from
Sep 25, 2023
104 changes: 99 additions & 5 deletions include/Graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class Graph {
private:
T_EdgeSet<T> edgeSet = {};

shared<AdjacencyMatrix<T>> cachedAdjMatrix;

// Private non-const getter for the set of nodes
std::unordered_set<shared<Node<T>>, nodeHash<T>> nodeSet();

Expand Down Expand Up @@ -130,7 +132,7 @@ class Graph {
#endif

public:
Graph() = default;
Graph();
Graph(const T_EdgeSet<T> &edgeSet);
virtual ~Graph() = default;
/**
Expand Down Expand Up @@ -248,7 +250,9 @@ class Graph {
* corrispondent to the link
* Note: No Thread Safe
*/
virtual const std::shared_ptr<AdjacencyMatrix<T>> getAdjMatrix() const;
virtual shared<AdjacencyMatrix<T>> getAdjMatrix() const;

virtual void getCacheAdjMatrix();
/**
* \brief This function generates a set of nodes linked to the provided node
* in a directed graph
Expand Down Expand Up @@ -733,11 +737,19 @@ class Graph {
const AdjacencyMatrix<T> &adj);
};

template <typename T>
Graph<T>::Graph() {
/* Caching the adjacency matrix */
getCacheAdjMatrix();
}

template <typename T>
Graph<T>::Graph(const T_EdgeSet<T> &edgeSet) {
for (auto edgeIt : edgeSet) {
this->edgeSet.insert(edgeIt);
}
/* Caching the adjacency matrix */
getCacheAdjMatrix();
}

template <typename T>
Expand All @@ -751,6 +763,8 @@ void Graph<T>::setEdgeSet(const T_EdgeSet<T> &edgeSet) {
for (auto edgeIt : edgeSet) {
this->edgeSet.insert(edgeIt);
}
/* Caching the adjacency matrix */
getCacheAdjMatrix();
}

template <typename T>
Expand All @@ -759,24 +773,47 @@ void Graph<T>::addEdge(const Edge<T> *edge) {
if (edge->isWeighted().has_value() && edge->isWeighted().value()) {
auto edge_shared = make_shared<DirectedWeightedEdge<T>>(*edge);
this->edgeSet.insert(edge_shared);
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem = {edge_shared->getNodePair().second, edge_shared};
(*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem));
} else {
auto edge_shared = make_shared<DirectedEdge<T>>(*edge);
this->edgeSet.insert(edge_shared);
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem = {edge_shared->getNodePair().second, edge_shared};
(*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem));
}
} else {
if (edge->isWeighted().has_value() && edge->isWeighted().value()) {
auto edge_shared = make_shared<UndirectedWeightedEdge<T>>(*edge);
this->edgeSet.insert(edge_shared);
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem = {edge_shared->getNodePair().second, edge_shared};
(*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem));
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem1 = {edge_shared->getNodePair().first, edge_shared};
(*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back(std::move(elem1));
} else {
auto edge_shared = make_shared<UndirectedEdge<T>>(*edge);
this->edgeSet.insert(edge_shared);
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem = {edge_shared->getNodePair().second, edge_shared};
(*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem));
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem1 = {edge_shared->getNodePair().first, edge_shared};
(*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back(std::move(elem1));
}
}
}

template <typename T>
void Graph<T>::addEdge(shared<const Edge<T>> edge) {
this->edgeSet.insert(edge);
/* Adding new edge in cached adjacency matrix */
if(edge.get()->isDirected().has_value() && edge.get()->isDirected().value()){
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem = {edge.get()->getNodePair().second, edge};
(*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back(std::move(elem));
}
else{
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem = {edge.get()->getNodePair().second, edge};
(*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back(std::move(elem));
std::pair<shared<const Node<T>>, shared<const Edge<T>>> elem1 = {edge.get()->getNodePair().first, edge};
(*cachedAdjMatrix)[edge.get()->getNodePair().second].push_back(std::move(elem1));
}
}

template <typename T>
Expand All @@ -788,6 +825,32 @@ void Graph<T>::removeEdge(const unsigned long long edgeId) {
[edgeOpt](const Edge<T> *edge) { return (*(edgeOpt.value()) == *edge); }));
*/
edgeSet.erase(edgeSet.find(edgeOpt.value()));
int delIndex = -1;
int i = 0;
/* Removing the edge from the cached adjacency matrix */
for(auto elem : (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first]){
if(elem.second.get()->getId() == edgeId){
delIndex = i;
break;
}
i++;
}
if(delIndex != -1){
(*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first].erase((*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first].begin()+delIndex);
}

delIndex = -1;
i = 0;
for(auto elem : (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second]){
if(elem.second.get()->getId() == edgeId){
delIndex = i;
break;
}
i++;
}
if(delIndex != -1){
(*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second].erase((*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second].begin()+delIndex);
}
}
}

Expand All @@ -805,7 +868,32 @@ bool Graph<T>::findEdge(shared<const Node<T>> v1, shared<const Node<T>> v2,
unsigned long long &id) const {
// This could be made faster by looking for the edge hash, assuming we hash
// based on node data, instead of a unique integer
for (auto e : this->edgeSet) {
if(cachedAdjMatrix.get() != NULL){
/* Searching for the edge using cached adjacency matrix */

for(auto elem : (*cachedAdjMatrix)[v1]){
if(elem.first == v2){
id = elem.second.get()->getId();
return true;
}
}
}
else{
/* Searching for the edge using edgeset */

for (auto e : this->edgeSet) {
if ((e->getNodePair().first == v1) && (e->getNodePair().second == v2)) {
id = e->getId();
return true;
}
if (!e->isDirected() &&
((e->getNodePair().second == v1) && (e->getNodePair().first == v2))) {
id = e->getId();
return true;
}
}
}
/*for (auto e : this->edgeSet) {
if ((e->getNodePair().first == v1) && (e->getNodePair().second == v2)) {
id = e->getId();
return true;
Expand All @@ -815,7 +903,7 @@ bool Graph<T>::findEdge(shared<const Node<T>> v1, shared<const Node<T>> v2,
id = e->getId();
return true;
}
}
}*/

id = 0;
return false;
Expand Down Expand Up @@ -1381,7 +1469,7 @@ std::shared_ptr<std::vector<Node<T>>> Graph<T>::eulerianPath() const {
}

template <typename T>
const std::shared_ptr<AdjacencyMatrix<T>> Graph<T>::getAdjMatrix() const {
shared<AdjacencyMatrix<T>> Graph<T>::getAdjMatrix() const {
auto adj = std::make_shared<AdjacencyMatrix<T>>();
auto addElementToAdjMatrix = [&adj](shared<const Node<T>> nodeFrom,
shared<const Node<T>> nodeTo,
Expand Down Expand Up @@ -1413,6 +1501,12 @@ const std::shared_ptr<AdjacencyMatrix<T>> Graph<T>::getAdjMatrix() const {
return adj;
}

template <typename T>
void Graph<T>::getCacheAdjMatrix() {
const auto adj = Graph<T>::getAdjMatrix();
this->cachedAdjMatrix = adj;
}

template <typename T>
const std::unordered_set<shared<const Node<T>>, nodeHash<T>>
Graph<T>::outNeighbors(const Node<T> *node) const {
Expand Down
27 changes: 27 additions & 0 deletions test/GraphTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,33 @@ TEST(GraphTest, GetNodeSet_2) {
}) != nodeSet.end());
}

TEST(GraphTest, FindEdge_Test) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add spacing in logical places between lines in this test. It's hard to read

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Please review.

CXXGraph::Node<int> node1("1", 1);
CXXGraph::Node<int> node2("2", 2);
CXXGraph::Node<int> node3("3", 3);
CXXGraph::Node<int> node4("4", 2);
CXXGraph::Node<int> node5("5", 3);
CXXGraph::UndirectedEdge<int> edge(1, node1, node2);
CXXGraph::UndirectedEdge<int> edge2(2, node2, node3);
CXXGraph::T_EdgeSet<int> edgeSet;
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge2));
CXXGraph::Graph<int> graph(edgeSet);
unsigned long long edgeId = 0;
ASSERT_TRUE(graph.findEdge(&node1,&node2,edgeId));
CXXGraph::UndirectedEdge<int> edge3(3, node1, node3);
graph.addEdge(make_shared<CXXGraph::UndirectedEdge<int>>(edge3));
ASSERT_TRUE(graph.findEdge(&node1,&node3,edgeId));
ASSERT_TRUE(graph.findEdge(&node3,&node1,edgeId));
CXXGraph::DirectedEdge<int> edge4(4, node1, node5);
graph.addEdge(make_shared<CXXGraph::DirectedEdge<int>>(edge4));
ASSERT_TRUE(graph.findEdge(&node1,&node5,edgeId));
ASSERT_FALSE(graph.findEdge(&node5,&node1,edgeId));
graph.removeEdge(2);
ASSERT_FALSE(graph.findEdge(&node2,&node3,edgeId));
ASSERT_FALSE(graph.findEdge(&node3,&node2,edgeId));
}

TEST(GraphTest, RawAddEdge_1) {
CXXGraph::Node<int> n1("a", 1);
CXXGraph::Node<int> n2("b", 1);
Expand Down