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
106 changes: 101 additions & 5 deletions include/Graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@
private:
T_EdgeSet<T> edgeSet = {};

std::shared_ptr<AdjacencyMatrix<T>> cachedAdjMatrix;
sbaldu marked this conversation as resolved.
Show resolved Hide resolved

// 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 @@
#endif

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

virtual std::shared_ptr<AdjacencyMatrix<T>> getCacheAdjMatrix();
sbaldu marked this conversation as resolved.
Show resolved Hide resolved
/**
* \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 @@
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 @@
for (auto edgeIt : edgeSet) {
this->edgeSet.insert(edgeIt);
}
/* Caching the adjacency matrix */
getCacheAdjMatrix();
}

template <typename T>
Expand All @@ -759,24 +773,47 @@
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));

Check warning on line 777 in include/Graph/Graph.hpp

View check run for this annotation

Codecov / codecov/patch

include/Graph/Graph.hpp#L776-L777

Added lines #L776 - L777 were not covered by tests
} 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));

Check warning on line 791 in include/Graph/Graph.hpp

View check run for this annotation

Codecov / codecov/patch

include/Graph/Graph.hpp#L788-L791

Added lines #L788 - L791 were not covered by tests
} 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 @@
[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 @@
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{
sbaldu marked this conversation as resolved.
Show resolved Hide resolved
/* 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;

Check warning on line 887 in include/Graph/Graph.hpp

View check run for this annotation

Codecov / codecov/patch

include/Graph/Graph.hpp#L884-L887

Added lines #L884 - L887 were not covered by tests
}
if (!e->isDirected() &&
((e->getNodePair().second == v1) && (e->getNodePair().first == v2))) {
id = e->getId();
return true;

Check warning on line 892 in include/Graph/Graph.hpp

View check run for this annotation

Codecov / codecov/patch

include/Graph/Graph.hpp#L889-L892

Added lines #L889 - L892 were not covered by tests
}
}
}
/*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 @@
id = e->getId();
return true;
}
}
}*/

id = 0;
return false;
Expand Down Expand Up @@ -1381,7 +1469,7 @@
}

template <typename T>
const std::shared_ptr<AdjacencyMatrix<T>> Graph<T>::getAdjMatrix() const {
std::shared_ptr<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,14 @@
return adj;
}

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

template <typename T>
const std::unordered_set<shared<const Node<T>>, nodeHash<T>>
Graph<T>::outNeighbors(const Node<T> *node) const {
Expand Down
30 changes: 30 additions & 0 deletions test/GraphTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,36 @@ 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);
std::pair<const CXXGraph::Node<int> *, const CXXGraph::Node<int> *> pairNode(
sbaldu marked this conversation as resolved.
Show resolved Hide resolved
&node1, &node2);
//CXXGraph::Edge<int> edge(1, pairNode);
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
Loading