diff --git a/examples/PartitionExample/partition_example.cpp b/examples/PartitionExample/partition_example.cpp index e359d9ce0..2c3b0e9d5 100644 --- a/examples/PartitionExample/partition_example.cpp +++ b/examples/PartitionExample/partition_example.cpp @@ -19,13 +19,13 @@ int main() { // std::cout << *cit_graph_ptr << std::endl; std::cout << cit_graph_ptr->getEdgeSet().size() << std::endl; std::cout << cit_graph_ptr->getNodeSet().size() << std::endl; - auto partitionedTwo = cit_graph_ptr->partitionGraph( + auto partitionedTwo = CXXGraph::Partitioning::Partitioner::partitionGraph( *cit_graph_ptr, CXXGraph::Partitioning::HDRF_ALG, 2, 1, 1, 1, 4); std::cout << "end partition two" << std::endl; - auto partitionedFour = cit_graph_ptr->partitionGraph( + auto partitionedFour = CXXGraph::Partitioning::Partitioner::partitionGraph( *cit_graph_ptr, CXXGraph::Partitioning::HDRF_ALG, 4, 1, 1, 1, 4); std::cout << "end partition four" << std::endl; - auto partitionedEight = cit_graph_ptr->partitionGraph( + auto partitionedEight = CXXGraph::Partitioning::Partitioner::partitionGraph( *cit_graph_ptr, CXXGraph::Partitioning::HDRF_ALG, 8, 1, 1, 1, 4); std::cout << "end partition eight" << std::endl; auto statsTwo = CXXGraph::Partitioning::getPartitionStats(partitionedTwo); diff --git a/include/CXXGraph/CXXGraph.hpp b/include/CXXGraph/CXXGraph.hpp index 4700ac8a5..e64fd9f87 100755 --- a/include/CXXGraph/CXXGraph.hpp +++ b/include/CXXGraph/CXXGraph.hpp @@ -2,14 +2,14 @@ #define __CXXGRAPH_H__ #include "CXXGraph/CXXGraphConfig.h" -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Graph/Graph.hpp" -#include "CXXGraph/Node/Node.hpp" +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Graph/Graph.h" +#include "CXXGraph/Node/Node.h" #include "CXXGraph/Partitioning/CoordinatedPartitionState.hpp" #include "CXXGraph/Partitioning/CoordinatedRecord.hpp" #include "CXXGraph/Partitioning/EBV.hpp" diff --git a/include/CXXGraph/Edge/DirectedEdge.h b/include/CXXGraph/Edge/DirectedEdge.h new file mode 100755 index 000000000..3ae44da60 --- /dev/null +++ b/include/CXXGraph/Edge/DirectedEdge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIRECTEDEDGE_H__ +#define __CXXGRAPH_DIRECTEDEDGE_H__ + +#pragma once + +#include "DirectedEdge_impl.hpp" + +#endif // __CXXGRAPH_DIRECTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/DirectedEdge_decl.h b/include/CXXGraph/Edge/DirectedEdge_decl.h new file mode 100644 index 000000000..a612eafb5 --- /dev/null +++ b/include/CXXGraph/Edge/DirectedEdge_decl.h @@ -0,0 +1,69 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIRECTEDEDGE_DECL_H__ +#define __CXXGRAPH_DIRECTEDEDGE_DECL_H__ + +#pragma once + +#include "Edge_decl.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared= std::shared_ptr; + +template +class UndirectedEdge; + +template +class DirectedEdge; +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const DirectedEdge &edge); +template +class DirectedEdge : public Edge { + public: + DirectedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2); + DirectedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2); + DirectedEdge(const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair); + DirectedEdge(const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair); + DirectedEdge(const Edge &edge); + virtual ~DirectedEdge() = default; + const Node &getFrom() const; + const Node &getTo() const; + const std::optional isDirected() const override; + const std::optional isWeighted() const override; + // operator + explicit operator UndirectedEdge() const { + return UndirectedEdge(Edge::getId(), Edge::getNodePair()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const DirectedEdge &edge); +}; +} // namespace CXXGraph + +#endif // __CXXGRAPH_DIRECTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/DirectedEdge.hpp b/include/CXXGraph/Edge/DirectedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 64% rename from include/CXXGraph/Edge/DirectedEdge.hpp rename to include/CXXGraph/Edge/DirectedEdge_impl.hpp index 50df9a709..46462752e --- a/include/CXXGraph/Edge/DirectedEdge.hpp +++ b/include/CXXGraph/Edge/DirectedEdge_impl.hpp @@ -17,57 +17,18 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_DIRECTEDEDGE_H__ -#define __CXXGRAPH_DIRECTEDEDGE_H__ +#ifndef __CXXGRAPH_DIRECTEDEDGE_IMPL_H__ +#define __CXXGRAPH_DIRECTEDEDGE_IMPL_H__ #pragma once -#include "Edge.hpp" +#include "DirectedEdge_decl.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; - + using std::make_unique; using std::make_shared; -template -class UndirectedEdge; - -template -class DirectedEdge; -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const DirectedEdge &edge); -template -class DirectedEdge : public Edge { - public: - DirectedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2); - DirectedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2); - DirectedEdge(const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair); - DirectedEdge(const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair); - DirectedEdge(const Edge &edge); - virtual ~DirectedEdge() = default; - const Node &getFrom() const; - const Node &getTo() const; - const std::optional isDirected() const override; - const std::optional isWeighted() const override; - // operator - explicit operator UndirectedEdge() const { - return UndirectedEdge(Edge::getId(), Edge::getNodePair()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const DirectedEdge &edge); -}; - template DirectedEdge::DirectedEdge(const CXXGraph::id_t id, const Node &node1, const Node &node2) @@ -122,4 +83,4 @@ std::ostream &operator<<(std::ostream &os, const DirectedEdge &edge) { } } // namespace CXXGraph -#endif // __CXXGRAPH_DIRECTEDEDGE_H__ +#endif // __CXXGRAPH_DIRECTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/DirectedWeightedEdge.h b/include/CXXGraph/Edge/DirectedWeightedEdge.h new file mode 100755 index 000000000..e5dd687c3 --- /dev/null +++ b/include/CXXGraph/Edge/DirectedWeightedEdge.h @@ -0,0 +1,26 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ +#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ +#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ + +#pragma once + +#include "DirectedWeightedEdge_impl.hpp" + +#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/DirectedWeightedEdge_decl.h b/include/CXXGraph/Edge/DirectedWeightedEdge_decl.h new file mode 100644 index 000000000..cea8cd2ff --- /dev/null +++ b/include/CXXGraph/Edge/DirectedWeightedEdge_decl.h @@ -0,0 +1,79 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ +#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_DECL_H__ +#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_DECL_H__ + +#pragma once + +#include "DirectedEdge_decl.h" +#include "Weighted.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared= std::shared_ptr; + +// Foward Declaration +template +class UndirectedWeightedEdge; + +template +class DirectedWeightedEdge; + +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const DirectedWeightedEdge &edge); + +template +class DirectedWeightedEdge : public DirectedEdge, public Weighted { + public: + DirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2, const double weight); + DirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2, const double weight); + DirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair, + const double weight); + DirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair, + const double weight); + DirectedWeightedEdge(const DirectedEdge &edge, const double weight); + DirectedWeightedEdge(const Edge &edge, const double weight); + DirectedWeightedEdge(const DirectedEdge &edge); + DirectedWeightedEdge(const Edge &edge); + DirectedWeightedEdge(const UndirectedWeightedEdge &edge); + virtual ~DirectedWeightedEdge() = default; + const std::optional isWeighted() const override; + // operator + explicit operator UndirectedWeightedEdge() const { + return UndirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), + Weighted::getWeight()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const DirectedWeightedEdge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_DECL_H__ diff --git a/include/CXXGraph/Edge/DirectedWeightedEdge.hpp b/include/CXXGraph/Edge/DirectedWeightedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 64% rename from include/CXXGraph/Edge/DirectedWeightedEdge.hpp rename to include/CXXGraph/Edge/DirectedWeightedEdge_impl.hpp index c0f2eaa07..c349450fc --- a/include/CXXGraph/Edge/DirectedWeightedEdge.hpp +++ b/include/CXXGraph/Edge/DirectedWeightedEdge_impl.hpp @@ -16,67 +16,19 @@ /***********************************************************/ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ -#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ +#ifndef __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_IMPL_H__ +#define __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_IMPL_H__ #pragma once -#include "DirectedEdge.hpp" -#include "Weighted.hpp" +#include "DirectedWeightedEdge_decl.h" +#include "Weighted.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; using std::make_unique; using std::make_shared; -// Foward Declaration -template -class UndirectedWeightedEdge; - -template -class DirectedWeightedEdge; - -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const DirectedWeightedEdge &edge); - -template -class DirectedWeightedEdge : public DirectedEdge, public Weighted { - public: - DirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2, const double weight); - DirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2, const double weight); - DirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair, - const double weight); - DirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair, - const double weight); - DirectedWeightedEdge(const DirectedEdge &edge, const double weight); - DirectedWeightedEdge(const Edge &edge, const double weight); - DirectedWeightedEdge(const DirectedEdge &edge); - DirectedWeightedEdge(const Edge &edge); - DirectedWeightedEdge(const UndirectedWeightedEdge &edge); - virtual ~DirectedWeightedEdge() = default; - const std::optional isWeighted() const override; - // operator - explicit operator UndirectedWeightedEdge() const { - return UndirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), - Weighted::getWeight()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const DirectedWeightedEdge &edge); -}; - template DirectedWeightedEdge::DirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, @@ -144,4 +96,4 @@ std::ostream &operator<<(std::ostream &os, } // namespace CXXGraph -#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_H__ +#endif // __CXXGRAPH_DIRECTEDWEIGHTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/Edge.h b/include/CXXGraph/Edge/Edge.h new file mode 100755 index 000000000..377886a39 --- /dev/null +++ b/include/CXXGraph/Edge/Edge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_EDGE_H__ +#define __CXXGRAPH_EDGE_H__ + +#pragma once + +#include "CXXGraph/Edge/Edge_impl.hpp" + +#endif // __CXXGRAPH_EDGE_H__ diff --git a/include/CXXGraph/Edge/Edge_decl.h b/include/CXXGraph/Edge/Edge_decl.h new file mode 100644 index 000000000..ac2d9cd71 --- /dev/null +++ b/include/CXXGraph/Edge/Edge_decl.h @@ -0,0 +1,74 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_EDGE_DECL_H__ +#define __CXXGRAPH_EDGE_DECL_H__ + +#pragma once + +#include +#include +#include + +#include "CXXGraph/Node/Node.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + + +template +class Edge; +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const Edge &edge); +template +class Edge { + private: + CXXGraph::id_t id = 0; + std::pair>, shared>> nodePair; + + public: + Edge(const CXXGraph::id_t id, const Node &node1, const Node &node2); + Edge(const CXXGraph::id_t id, shared> node1, shared> node2); + Edge(const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair); + Edge(const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair); + virtual ~Edge() = default; + void setFirstNode(shared> node); + void setSecondNode(shared> node); + const unsigned long long getId() const; + const std::pair>, shared>> &getNodePair() const; + shared> getOtherNode(shared> node) const; + virtual const std::optional isDirected() const; + virtual const std::optional isWeighted() const; + // operator + virtual bool operator==(const Edge &b) const; + bool operator<(const Edge &b) const; + + friend std::ostream &operator<< <>(std::ostream &os, const Edge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_EDGE_DECL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Edge/Edge.hpp b/include/CXXGraph/Edge/Edge_impl.hpp old mode 100755 new mode 100644 similarity index 69% rename from include/CXXGraph/Edge/Edge.hpp rename to include/CXXGraph/Edge/Edge_impl.hpp index a846c400e..c525679e7 --- a/include/CXXGraph/Edge/Edge.hpp +++ b/include/CXXGraph/Edge/Edge_impl.hpp @@ -17,17 +17,12 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_EDGE_H__ -#define __CXXGRAPH_EDGE_H__ +#ifndef __CXXGRAPH_EDGE_IMPL_H__ +#define __CXXGRAPH_EDGE_IMPL_H__ #pragma once -#include -#include -#include - -#include "CXXGraph/Node/Node.hpp" -#include "CXXGraph/Utility/id_t.hpp" +#include "CXXGraph/Edge/Edge_decl.h" namespace CXXGraph { // Smart pointers alias @@ -39,42 +34,6 @@ using shared = std::shared_ptr; using std::make_unique; using std::make_shared; -template -class Edge; -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const Edge &edge); -template -class Edge { - private: - CXXGraph::id_t id = 0; - std::pair>, shared>> nodePair; - - public: - Edge(const CXXGraph::id_t id, const Node &node1, const Node &node2); - Edge(const CXXGraph::id_t id, shared> node1, shared> node2); - Edge(const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair); - Edge(const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair); - virtual ~Edge() = default; - void setFirstNode(shared> node); - void setSecondNode(shared> node); - const unsigned long long getId() const; - const std::pair>, shared>> &getNodePair() const; - shared> getOtherNode(shared> node) const; - virtual const std::optional isDirected() const; - virtual const std::optional isWeighted() const; - // operator - virtual bool operator==(const Edge &b) const; - bool operator<(const Edge &b) const; - // operator DirectedEdge() const { return DirectedEdge(id, nodePair); } - // operator UndirectedEdge() const { return UndirectedEdge(id, - // nodePair); } - - friend std::ostream &operator<< <>(std::ostream &os, const Edge &edge); -}; - template Edge::Edge(const CXXGraph::id_t id, const Node &node1, const Node &node2) { @@ -166,4 +125,4 @@ std::ostream &operator<<(std::ostream &os, const Edge &edge) { } } // namespace CXXGraph -#endif // __CXXGRAPH_EDGE_H__ +#endif // __CXXGRAPH_EDGE_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Edge/UndirectedEdge.h b/include/CXXGraph/Edge/UndirectedEdge.h new file mode 100755 index 000000000..6d15b7f22 --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedEdge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDEDGE_H__ +#define __CXXGRAPH_UNDIRECTEDEDGE_H__ + +#pragma once + +#include "UndirectedEdge_impl.hpp" + +#endif // __CXXGRAPH_UNDIRECTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/UndirectedEdge_decl.h b/include/CXXGraph/Edge/UndirectedEdge_decl.h new file mode 100644 index 000000000..efd7b2f36 --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedEdge_decl.h @@ -0,0 +1,69 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDEDGE_DECL_H__ +#define __CXXGRAPH_UNDIRECTEDEDGE_DECL_H__ + +#pragma once + +#include "Edge_decl.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared= std::shared_ptr; + +template +class UndirectedEdge; + +// ostream operator +template +std::ostream &operator<<(std::ostream &o, const UndirectedEdge &edge); + +template +class UndirectedEdge : public Edge { + public: + UndirectedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2); + UndirectedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2); + UndirectedEdge(const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair); + UndirectedEdge(const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair); + UndirectedEdge(const Edge &edge); + virtual ~UndirectedEdge() = default; + const Node &getNode1() const; + const Node &getNode2() const; + const std::optional isDirected() const override; + const std::optional isWeighted() const override; + // operator + explicit operator DirectedEdge() const { + return DirectedEdge(Edge::getId(), Edge::getNodePair()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const UndirectedEdge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_UNDIRECTEDEDGE_DECL_H__ diff --git a/include/CXXGraph/Edge/UndirectedEdge.hpp b/include/CXXGraph/Edge/UndirectedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 65% rename from include/CXXGraph/Edge/UndirectedEdge.hpp rename to include/CXXGraph/Edge/UndirectedEdge_impl.hpp index 6678656bc..2293e5a53 --- a/include/CXXGraph/Edge/UndirectedEdge.hpp +++ b/include/CXXGraph/Edge/UndirectedEdge_impl.hpp @@ -17,56 +17,18 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_UNDIRECTEDEDGE_H__ -#define __CXXGRAPH_UNDIRECTEDEDGE_H__ +#ifndef __CXXGRAPH_UNDIRECTEDEDGE_IMPL_H__ +#define __CXXGRAPH_UNDIRECTEDEDGE_IMPL_H__ #pragma once -#include "Edge.hpp" +#include "UndirectedEdge_decl.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; using std::make_unique; using std::make_shared; -template -class UndirectedEdge; - -// ostream operator -template -std::ostream &operator<<(std::ostream &o, const UndirectedEdge &edge); - -template -class UndirectedEdge : public Edge { - public: - UndirectedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2); - UndirectedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2); - UndirectedEdge(const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair); - UndirectedEdge(const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair); - UndirectedEdge(const Edge &edge); - virtual ~UndirectedEdge() = default; - const Node &getNode1() const; - const Node &getNode2() const; - const std::optional isDirected() const override; - const std::optional isWeighted() const override; - // operator - explicit operator DirectedEdge() const { - return DirectedEdge(Edge::getId(), Edge::getNodePair()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const UndirectedEdge &edge); -}; - template UndirectedEdge::UndirectedEdge(const CXXGraph::id_t id, const Node &node1, const Node &node2) @@ -122,4 +84,4 @@ std::ostream &operator<<(std::ostream &os, const UndirectedEdge &edge) { } } // namespace CXXGraph -#endif // __CXXGRAPH_UNDIRECTEDEDGE_H__ +#endif // __CXXGRAPH_UNDIRECTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/UndirectedWeightedEdge.h b/include/CXXGraph/Edge/UndirectedWeightedEdge.h new file mode 100755 index 000000000..7f7e28cca --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedWeightedEdge.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ +#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ + +#pragma once + +#include "UndirectedWeightedEdge_impl.hpp" + +#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ diff --git a/include/CXXGraph/Edge/UndirectedWeightedEdge_decl.h b/include/CXXGraph/Edge/UndirectedWeightedEdge_decl.h new file mode 100644 index 000000000..b0d0b9565 --- /dev/null +++ b/include/CXXGraph/Edge/UndirectedWeightedEdge_decl.h @@ -0,0 +1,81 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_DECL_H__ +#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_DECL_H__ + +#pragma once + +#include "UndirectedEdge_decl.h" +#include "Weighted.h" + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +// Foward Declaration +template +class DirectedWeightedEdge; + +template +class UndirectedWeightedEdge; + +// ostream operator +template +std::ostream &operator<<(std::ostream &o, + const UndirectedWeightedEdge &edge); + +template +class UndirectedWeightedEdge : public UndirectedEdge, public Weighted { + public: + UndirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, + const Node &node2, const double weight); + UndirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, + shared> node2, const double weight); + UndirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair *, const Node *> &nodepair, + const double weight); + UndirectedWeightedEdge( + const CXXGraph::id_t id, + const std::pair>, shared>> &nodepair, + const double weight); + UndirectedWeightedEdge(const UndirectedEdge &edge, const double weight); + UndirectedWeightedEdge(const Edge &edge, const double weight); + UndirectedWeightedEdge(const UndirectedEdge &edge); + UndirectedWeightedEdge(const Edge &edge); + UndirectedWeightedEdge(const DirectedWeightedEdge &edge); + virtual ~UndirectedWeightedEdge() = default; + const std::optional isWeighted() const override; + // operator + explicit operator DirectedWeightedEdge() const { + return DirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), + Weighted::getWeight()); + } + + friend std::ostream &operator<< <>(std::ostream &os, + const UndirectedWeightedEdge &edge); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_DECL_H__ diff --git a/include/CXXGraph/Edge/UndirectedWeightedEdge.hpp b/include/CXXGraph/Edge/UndirectedWeightedEdge_impl.hpp old mode 100755 new mode 100644 similarity index 64% rename from include/CXXGraph/Edge/UndirectedWeightedEdge.hpp rename to include/CXXGraph/Edge/UndirectedWeightedEdge_impl.hpp index 97b4ae2b7..b93805b35 --- a/include/CXXGraph/Edge/UndirectedWeightedEdge.hpp +++ b/include/CXXGraph/Edge/UndirectedWeightedEdge_impl.hpp @@ -17,68 +17,18 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ -#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ +#ifndef __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_IMPL_H__ +#define __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_IMPL_H__ #pragma once -#include "UndirectedEdge.hpp" -#include "Weighted.hpp" +#include "UndirectedWeightedEdge_decl.h" namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared= std::shared_ptr; using std::make_unique; using std::make_shared; -// Foward Declaration -template -class DirectedWeightedEdge; - -template -class UndirectedWeightedEdge; - -// ostream operator -template -std::ostream &operator<<(std::ostream &o, - const UndirectedWeightedEdge &edge); - -template -class UndirectedWeightedEdge : public UndirectedEdge, public Weighted { - public: - UndirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, - const Node &node2, const double weight); - UndirectedWeightedEdge(const CXXGraph::id_t id, shared> node1, - shared> node2, const double weight); - UndirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair *, const Node *> &nodepair, - const double weight); - UndirectedWeightedEdge( - const CXXGraph::id_t id, - const std::pair>, shared>> &nodepair, - const double weight); - UndirectedWeightedEdge(const UndirectedEdge &edge, const double weight); - UndirectedWeightedEdge(const Edge &edge, const double weight); - UndirectedWeightedEdge(const UndirectedEdge &edge); - UndirectedWeightedEdge(const Edge &edge); - UndirectedWeightedEdge(const DirectedWeightedEdge &edge); - virtual ~UndirectedWeightedEdge() = default; - const std::optional isWeighted() const override; - // operator - explicit operator DirectedWeightedEdge() const { - return DirectedWeightedEdge(Edge::getId(), Edge::getNodePair(), - Weighted::getWeight()); - } - - friend std::ostream &operator<< <>(std::ostream &os, - const UndirectedWeightedEdge &edge); -}; - template UndirectedWeightedEdge::UndirectedWeightedEdge(const CXXGraph::id_t id, const Node &node1, @@ -146,4 +96,4 @@ std::ostream &operator<<(std::ostream &os, } // namespace CXXGraph -#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_H__ +#endif // __CXXGRAPH_UNDIRECTEDWEIGHTEDEDGE_IMPL_H__ diff --git a/include/CXXGraph/Edge/Weighted.h b/include/CXXGraph/Edge/Weighted.h new file mode 100755 index 000000000..c657e5051 --- /dev/null +++ b/include/CXXGraph/Edge/Weighted.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_WEIGHTED_H__ +#define __CXXGRAPH_WEIGHTED_H__ + +#pragma once + +#include "Weighted_impl.hpp" + +#endif // __CXXGRAPH_WEIGHTED_H__ \ No newline at end of file diff --git a/include/CXXGraph/Edge/Weighted_decl.h b/include/CXXGraph/Edge/Weighted_decl.h new file mode 100644 index 000000000..5b16a1aa1 --- /dev/null +++ b/include/CXXGraph/Edge/Weighted_decl.h @@ -0,0 +1,39 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_WEIGHTED_DECL_H__ +#define __CXXGRAPH_WEIGHTED_DECL_H__ + +#pragma once + +namespace CXXGraph { +class Weighted { + private: + double weight = 0.0; + + public: + Weighted(); + explicit Weighted(const double weight); + virtual ~Weighted() = default; + double getWeight() const; + void setWeight(const double weight); +}; +} // namespace CXXGraph + +#endif // __CXXGRAPH_WEIGHTED_DECL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Edge/Weighted.hpp b/include/CXXGraph/Edge/Weighted_impl.hpp old mode 100755 new mode 100644 similarity index 83% rename from include/CXXGraph/Edge/Weighted.hpp rename to include/CXXGraph/Edge/Weighted_impl.hpp index cb6b717fd..327d88710 --- a/include/CXXGraph/Edge/Weighted.hpp +++ b/include/CXXGraph/Edge/Weighted_impl.hpp @@ -17,23 +17,14 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_WEIGHTED_H__ -#define __CXXGRAPH_WEIGHTED_H__ +#ifndef __CXXGRAPH_WEIGHTED_IMPL_H__ +#define __CXXGRAPH_WEIGHTED_IMPL_H__ #pragma once +#include "Weighted_decl.h" + namespace CXXGraph { -class Weighted { - private: - double weight = 0.0; - - public: - Weighted(); - explicit Weighted(const double weight); - virtual ~Weighted() = default; - double getWeight() const; - void setWeight(const double weight); -}; // inline because the implementation of non-template function in header file inline Weighted::Weighted() { weight = 0.0; } @@ -49,4 +40,4 @@ inline void Weighted::setWeight(const double weight) { this->weight = weight; } } // namespace CXXGraph -#endif // __CXXGRAPH_WEIGHTED_H__ \ No newline at end of file +#endif // __CXXGRAPH_WEIGHTED_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp b/include/CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp new file mode 100644 index 000000000..cbb6fd036 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp @@ -0,0 +1,129 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BELLMANFORD_IMPL_H__ +#define __CXXGRAPH_BELLMANFORD_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +const BellmanFordResult Graph::bellmanford(const Node &source, + const Node &target) const { + BellmanFordResult result; + result.success = false; + result.errorMessage = ""; + result.result = INF_DOUBLE; + auto nodeSet = Graph::getNodeSet(); + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist, + currentDist; + // n denotes the number of vertices in graph + auto n = nodeSet.size(); + for (const auto &elem : nodeSet) { + dist[elem] = INF_DOUBLE; + currentDist[elem] = INF_DOUBLE; + } + + // marking the distance of source as 0 + dist[*source_node_it] = 0; + // set if node distances in two consecutive + // iterations remain the same. + auto earlyStopping = false; + // outer loop for vertex relaxation + for (int i = 0; i < n - 1; ++i) { + auto edgeSet = Graph::getEdgeSet(); + // inner loop for distance updates of + // each relaxation + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edge_weight = + (std::dynamic_pointer_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) + dist[elem.second] = dist[elem.first] + edge_weight; + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + auto flag = true; + for (const auto &[key, value] : dist) { + if (currentDist[key] != value) { + flag = false; + break; + } + } + for (const auto &[key, value] : dist) { + currentDist[key] = value; // update the current distance + } + if (flag) { + earlyStopping = true; + break; + } + } + + // check if there exists a negative cycle + if (!earlyStopping) { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edge_weight = + (std::dynamic_pointer_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) { + result.success = true; + result.negativeCycle = true; + result.errorMessage = ""; + return result; + } + } + } + + if (dist[*target_node_it] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.negativeCycle = false; + result.result = dist[*target_node_it]; + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + result.result = -1; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_BELLMANFORD_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp b/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp new file mode 100644 index 000000000..38bafc77e --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp @@ -0,0 +1,261 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BESTFIRSTSEARCH_IMPL_H__ +#define __CXXGRAPH_BESTFIRSTSEARCH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" +#include +#include +#include +#include +#include + +namespace CXXGraph { +template +BestFirstSearchResult Graph::best_first_search( + const Node &source, const Node &target) const { + BestFirstSearchResult result; + auto &nodeSet = Graph::getNodeSet(); + using pq_type = std::pair>>; + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + + std::priority_queue, std::greater> pq; + + std::vector> visited; + visited.push_back(source); + pq.push(std::make_pair(0.0, *source_node_it)); + + while (!pq.empty()) { + shared> currentNode = pq.top().second; + pq.pop(); + result.nodesInBestSearchOrder.push_back(*currentNode); + + if (*currentNode == target) { + break; + } + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(currentNode)) { + if (elem.second->isWeighted().has_value()) { + if (elem.second->isDirected().has_value()) { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + } + } else { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + } + } + } else { + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + result.nodesInBestSearchOrder.clear(); + return result; + } + } + } + } + + result.success = true; + return result; +} + +template +const std::vector> Graph::concurrency_breadth_first_search( + const Node &start, size_t num_threads) const { + std::vector> bfs_result; + // check is exist node in the graph + auto &nodeSet = Graph::getNodeSet(); + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return bfs_result; + } + + std::unordered_map>, size_t, nodeHash> node_to_index; + for (const auto &node : nodeSet) { + node_to_index[node] = node_to_index.size(); + } + std::vector visited(nodeSet.size(), 0); + + // parameter limitations + if (num_threads <= 0) { + std::cout << "Error: number of threads should be greater than 0" + << std::endl; + num_threads = 2; + } + + // vector that stores vertices to be visit + std::vector>> level_tracker, next_level_tracker; + level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + + // mark the starting node as visited + visited[node_to_index[*start_node_it]] = 1; + level_tracker.push_back(*start_node_it); + + // a worker is assigned a small part of tasks for each time + // assignments of tasks in current level and updates of tasks in next + // level are inclusive + std::mutex tracker_mutex; + std::mutex next_tracker_mutex; + std::atomic assigned_tasks = 0; + int num_tasks = 1; + // unit of task assignment, which mean assign block_size tasks to a + // worker each time + int block_size = 1; + int level = 1; + + auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, + &num_tasks, &block_size]() -> std::pair { + /* + std::lock_guard tracker_guard(tracker_mutex); + int task_block_size = std::min(num_tasks - assigned_tasks, + block_size); std::pair task_block{assigned_tasks, + assigned_tasks + task_block_size}; assigned_tasks += task_block_size; + return task_block; + */ + int start = assigned_tasks.fetch_add(block_size); + int end = std::min(num_tasks, start + block_size); + return {start, end}; + }; + + auto submit_result = + [&next_level_tracker, &next_tracker_mutex]( + std::vector>> &submission) -> void { + std::lock_guard tracker_guard(next_tracker_mutex); + next_level_tracker.insert(std::end(next_level_tracker), + std::begin(submission), std::end(submission)); + }; + + // worker thread sleep until it begin to search nodes of next level + std::mutex next_level_mutex; + std::condition_variable next_level_cond; + std::atomic waiting_workers = 0; + + auto bfs_worker = [&]() -> void { + // algorithm is not done + while (!level_tracker.empty()) { + // search for nodes in a level is not done + std::vector>> local_tracker; + while (true) { + auto [start_index, end_index] = extract_tasks(); + if (start_index >= end_index) { + break; + } + + for (int i = start_index; i < end_index; ++i) { + if (cachedAdjMatrix->count(level_tracker[i])) { + for (const auto &elem : cachedAdjMatrix->at(level_tracker[i])) { + int index = (int)node_to_index[elem.first]; + if (visited[index] == 0) { + visited[index] = 1; + local_tracker.push_back(elem.first); + } + } + } + } + } + + // submit local result to global result + if (!local_tracker.empty()) { + submit_result(local_tracker); + } + + // last worker need to do preparation for the next iteration + int cur_level = level; + if (num_threads == 1 + waiting_workers.fetch_add(1)) { + swap(level_tracker, next_level_tracker); + next_level_tracker.clear(); + + // adjust block_size according to number of nodes in next level + block_size = 4; + if (level_tracker.size() <= num_threads * 4) { + block_size = std::max( + 1, static_cast(std::ceil( + static_cast(level_tracker.size()) / num_threads))); + } else if (level_tracker.size() >= num_threads * 64) { + block_size = 16; + } + + num_tasks = (int)level_tracker.size(); + waiting_workers = 0; + assigned_tasks = 0; + level = level + 1; + next_level_cond.notify_all(); + } else { + // not to wait if last worker reachs last statement before notify + // all or even further + std::unique_lock next_level_lock(next_level_mutex); + next_level_cond.wait(next_level_lock, [&level, cur_level]() { + return level != cur_level; + }); + } + } + }; + + std::vector workers; + for (int i = 0; i < num_threads - 1; ++i) { + workers.emplace_back(std::thread(bfs_worker)); + } + bfs_worker(); + + for (auto &worker : workers) { + if (worker.joinable()) { + worker.join(); + } + } + + for (const auto &visited_node : nodeSet) { + if (visited[node_to_index[visited_node]] != 0) { + bfs_result.push_back(*visited_node); + } + } + + return bfs_result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_BESTFIRSTSEARCH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp b/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp new file mode 100644 index 000000000..b43853977 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp @@ -0,0 +1,126 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BORUVKA_IMPL_H__ +#define __CXXGRAPH_BORUVKA_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" +#include + +namespace CXXGraph { + +template +const MstResult Graph::boruvka() const { + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + const auto nodeSet = Graph::getNodeSet(); + const auto n = nodeSet.size(); + + // Use std map for storing n subsets. + auto subsets = make_shared>(); + + // Initially there are n different trees. + // Finally there will be one tree that will be MST + auto numTrees = n; + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + const auto edgeSet = Graph::getEdgeSet(); + std::unordered_map edgeWeight; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) + edgeWeight[edge->getId()] = + (std::dynamic_pointer_cast(edge))->getWeight(); + else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + (*subsets)[node->getId()] = set; + } + + result.mstCost = 0; // we will store the cost here + // exit when only 1 tree i.e. mst + while (numTrees > 1) { + // Everytime initialize cheapest map + // It stores index of the cheapest edge of subset. + std::unordered_map cheapest; + for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; + + // Traverse through all edges and update + // cheapest of every component + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edgeId = edge->getId(); + // Find sets of two corners of current edge + auto set1 = Graph::setFind(subsets, elem.first->getId()); + auto set2 = Graph::setFind(subsets, elem.second->getId()); + + // If two corners of current edge belong to + // same set, ignore current edge + if (set1 == set2) continue; + + // Else check if current edge is closer to previous + // cheapest edges of set1 and set2 + if (cheapest[set1] == INT_MAX || + edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) + cheapest[set1] = edgeId; + + if (cheapest[set2] == INT_MAX || + edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) + cheapest[set2] = edgeId; + } + + // iterate over all the vertices and add picked + // cheapest edges to MST + for (const auto &[nodeId, edgeId] : cheapest) { + // Check if cheapest for current set exists + if (edgeId != INT_MAX) { + auto cheapestNode = Graph::getEdge(edgeId).value()->getNodePair(); + auto set1 = Graph::setFind(subsets, cheapestNode.first->getId()); + auto set2 = Graph::setFind(subsets, cheapestNode.second->getId()); + if (set1 == set2) continue; + result.mstCost += edgeWeight[edgeId]; + auto newEdgeMST = std::make_pair(cheapestNode.first->getUserId(), + cheapestNode.second->getUserId()); + result.mst.push_back(newEdgeMST); + // take union of set1 and set2 and decrease number of trees + Graph::setUnion(subsets, set1, set2); + numTrees--; + } + } + } + result.success = true; + return result; +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_BORUVKA_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp b/include/CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp new file mode 100644 index 000000000..08ec41058 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp @@ -0,0 +1,69 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BREADTHFIRSTSEARCH_IMPL_H__ +#define __CXXGRAPH_BREADTHFIRSTSEARCH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const std::vector> Graph::breadth_first_search( + const Node &start) const { + // vector to keep track of visited nodes + std::vector> visited; + auto &nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return visited; + } + // queue that stores vertices that need to be further explored + std::queue>> tracker; + + // mark the starting node as visited + visited.push_back(start); + tracker.push(*start_node_it); + while (!tracker.empty()) { + shared> node = tracker.front(); + tracker.pop(); + if (cachedAdjMatrix->find(node) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(node)) { + // if the node is not visited then mark it as visited + // and push it to the queue + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + tracker.push(elem.first); + } + } + } + } + + return visited; +} + + +} // namespace CXXGraph +#endif // __CXXGRAPH_BREADTHFIRSTSEARCH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Connectivity_impl.hpp b/include/CXXGraph/Graph/Algorithm/Connectivity_impl.hpp new file mode 100644 index 000000000..9783fe3ff --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Connectivity_impl.hpp @@ -0,0 +1,109 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_CONNECTIVITY_IMPL_H__ +#define __CXXGRAPH_CONNECTIVITY_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +bool Graph::isConnectedGraph() const { + if (!isUndirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function>)> dfs_helper = + [this, &visited, &dfs_helper](shared> source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + }; + // call dfs_helper for the first node + dfs_helper(*(nodeSet.begin())); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; + } + } + return true; + } +} + +template +bool Graph::isStronglyConnectedGraph() const { + if (!isDirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + for (const auto &start_node : nodeSet) { + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function>)> dfs_helper = + [this, &visited, &dfs_helper](shared> source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + }; + // call dfs_helper for the first node + dfs_helper(start_node); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; + } + } + } + return true; + } +} +} // namespace CXXGraph +#endif // __CXXGRAPH_CONNECTIVITY_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp b/include/CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp new file mode 100644 index 000000000..a66f5e2a0 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp @@ -0,0 +1,231 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_CYCLEDETECTION_IMPL_H__ +#define __CXXGRAPH_CYCLEDETECTION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +bool Graph::isCyclicDirectedGraphDFS() const { + if (!isDirectedGraph()) { + return false; + } + enum nodeStates : uint8_t { not_visited, in_stack, visited }; + auto nodeSet = Graph::getNodeSet(); + + /* State of the node. + * + * It is a vector of "nodeStates" which represents the state node is in. + * It can take only 3 values: "not_visited", "in_stack", and "visited". + * + * Initially, all nodes are in "not_visited" state. + */ + std::unordered_map state; + for (const auto &node : nodeSet) { + state[node->getId()] = not_visited; + } + int stateCounter = 0; + + // Start visiting each node. + for (const auto &node : nodeSet) { + // If a node is not visited, only then check for presence of cycle. + // There is no need to check for presence of cycle for a visited + // node as it has already been checked for presence of cycle. + if (state[node->getId()] == not_visited) { + // Check for cycle. + std::function>, + std::unordered_map &, + shared>)> + isCyclicDFSHelper; + isCyclicDFSHelper = + [this, &isCyclicDFSHelper]( + const std::shared_ptr> adjMatrix, + std::unordered_map &states, + shared> node) { + // Add node "in_stack" state. + states[node->getId()] = in_stack; + + // If the node has children, then recursively visit all + // children of the node. + auto const it = adjMatrix->find(node); + if (it != adjMatrix->end()) { + for (const auto &child : it->second) { + // If state of child node is "not_visited", evaluate that + // child for presence of cycle. + auto state_of_child = states.at((std::get<0>(child))->getId()); + if (state_of_child == not_visited) { + if (isCyclicDFSHelper(adjMatrix, states, + std::get<0>(child))) { + return true; + } + } else if (state_of_child == in_stack) { + // If child node was "in_stack", then that means that + // there is a cycle in the graph. Return true for + // presence of the cycle. + return true; + } + } + } + + // Current node has been evaluated for the presence of cycle + // and had no cycle. Mark current node as "visited". + states[node->getId()] = visited; + // Return that current node didn't result in any cycles. + return false; + }; + if (isCyclicDFSHelper(cachedAdjMatrix, state, node)) { + return true; + } + } + } + + // All nodes have been safely traversed, that means there is no cycle in + // the graph. Return false. + return false; +} + +template +bool Graph::containsCycle(const T_EdgeSet *edgeSet) const { + auto edgeSet_ptr = make_shared>(*edgeSet); + auto subset = make_shared>(); + // initialize the subset parent and rank values + for (const auto &edge : *edgeSet_ptr) { + auto &[first, second] = edge->getNodePair(); + std::vector nodeId(2); + nodeId.push_back(first->getId()); + nodeId.push_back(second->getId()); + for (const auto &id : nodeId) { + auto nodeExists = [id](const auto &it) { + return (id == (it.second).parent); + }; + + if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == + (*subset).end()) { + Subset set; + set.parent = id; + set.rank = 0; + (*subset)[id] = set; + } + } + } + return Graph::containsCycle(edgeSet_ptr, subset); +} + +template +bool Graph::containsCycle(shared> edgeSet) const { + auto subset = make_shared>(); + // initialize the subset parent and rank values + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + std::vector nodeId(2); + nodeId.push_back(first->getId()); + nodeId.push_back(second->getId()); + for (const auto &id : nodeId) { + auto nodeExists = [id](const auto &it) { + return (id == (it.second).parent); + }; + + if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == + (*subset).end()) { + Subset set; + set.parent = id; + set.rank = 0; + (*subset)[id] = set; + } + } + } + return Graph::containsCycle(edgeSet, subset); +} + +template +bool Graph::containsCycle( + shared> edgeSet, + shared> subset) const { + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + auto set1 = Graph::setFind(subset, first->getId()); + auto set2 = Graph::setFind(subset, second->getId()); + if (set1 == set2) return true; + Graph::setUnion(subset, set1, set2); + } + return false; +} + +template +bool Graph::isCyclicDirectedGraphBFS() const { + if (!isDirectedGraph()) { + return false; + } + auto nodeSet = Graph::getNodeSet(); + + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + // Calculate the indegree i.e. the number of incident edges to the node. + for (auto const &list : (*cachedAdjMatrix)) { + auto children = list.second; + for (auto const &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } + + std::queue>> can_be_solved; + for (const auto &node : nodeSet) { + // If a node doesn't have any input edges, then that node will + // definately not result in a cycle and can be visited safely. + if (!indegree[node->getId()]) { + can_be_solved.emplace(node); + } + } + + // Vertices that need to be traversed. + auto remain = nodeSet.size(); + // While there are safe nodes that we can visit. + while (!can_be_solved.empty()) { + auto solved = can_be_solved.front(); + // Visit the node. + can_be_solved.pop(); + // Decrease number of nodes that need to be traversed. + remain--; + + // Visit all the children of the visited node. + auto it = cachedAdjMatrix->find(solved); + if (it != cachedAdjMatrix->end()) { + for (const auto &child : it->second) { + // Check if we can visited the node safely. + if (--indegree[std::get<0>(child)->getId()] == 0) { + // if node can be visited safely, then add that node to + // the visit queue. + can_be_solved.emplace(std::get<0>(child)); + } + } + } + } + + // If there are still nodes that we can't visit, then it means that + // there is a cycle and return true, else return false. + return !(remain == 0); +} +} // namespace CXXGraph +#endif // __CXXGRAPH_CYCLEDETECTION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp b/include/CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp new file mode 100644 index 000000000..82a0553da --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp @@ -0,0 +1,64 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DEPTHFIRSTSEARCH_IMPL_H__ +#define __CXXGRAPH_DEPTHFIRSTSEARCH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const std::vector> Graph::depth_first_search( + const Node &start) const { + // vector to keep track of visited nodes + std::vector> visited; + auto nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return visited; + } + std::function>, + shared>, std::vector> &)> + explore; + explore = [&explore](const std::shared_ptr> adj, + shared> node, + std::vector> &visited) -> void { + visited.push_back(*node); + if (adj->find(node) != adj->end()) { + for (const auto &x : adj->at(node)) { + if (std::find(visited.begin(), visited.end(), *(x.first)) == + visited.end()) { + explore(adj, x.first, visited); + } + } + } + }; + explore(cachedAdjMatrix, *start_node_it, visited); + + return visited; +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_DEPTHFIRSTSEARCH_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp b/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp new file mode 100644 index 000000000..ffebbc370 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Dial_impl.hpp @@ -0,0 +1,158 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIAL_IMPL_H__ +#define __CXXGRAPH_DIAL_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const DialResult Graph::dial(const Node &source, int maxWeight) const { + DialResult result; + result.success = false; + + auto nodeSet = getNodeSet(); + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + /* With each distance, iterator to that vertex in + its bucket is stored so that vertex can be deleted + in O(1) at time of updation. So + dist[i].first = distance of ith vertex from src vertex + dits[i].second = vertex i in bucket number */ + auto V = nodeSet.size(); + std::unordered_map>, + std::pair>>, nodeHash> + dist; + + // Initialize all distances as infinite (INF) + for (const auto &node : nodeSet) { + dist[node].first = std::numeric_limits::max(); + } + + // Create buckets B[]. + // B[i] keep vertex of distance label i + std::vector>>> B((maxWeight * V + 1)); + + B[0].push_back(*source_node_it); + dist[*source_node_it].first = 0; + + int idx = 0; + while (true) { + // Go sequentially through buckets till one non-empty + // bucket is found + while (idx < B.size() && B[idx].size() == 0u && idx < maxWeight * V) { + idx++; + } + + // If all buckets are empty, we are done. + if (idx == maxWeight * V) { + break; + } + + // Take top vertex from bucket and pop it + auto u = B[idx].front(); + B[idx].pop_front(); + + // Process all adjacents of extracted vertex 'u' and + // update their distanced if required. + for (const auto &i : (*cachedAdjMatrix)[u]) { + auto v = i.first; + int weight = 0; + if (i.second->isWeighted().has_value() && + i.second->isWeighted().value()) { + if (i.second->isDirected().has_value() && + i.second->isDirected().value()) { + shared> dw_edge = + std::static_pointer_cast>(i.second); + weight = (int)dw_edge->getWeight(); + } else if (i.second->isDirected().has_value() && + !i.second->isDirected().value()) { + shared> udw_edge = + std::static_pointer_cast>( + i.second); + weight = (int)udw_edge->getWeight(); + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + auto u_i = std::find_if( + dist.begin(), dist.end(), + [u](std::pair>, + std::pair>>> const &it) { + return (*u == *(it.first)); + }); + + auto v_i = std::find_if( + dist.begin(), dist.end(), + [v](std::pair>, + std::pair>>> const &it) { + return (*v == *(it.first)); + }); + long du = u_i->second.first; + long dv = v_i->second.first; + + // If there is shorted path to v through u. + if (dv > du + weight) { + // If dv is not INF then it must be in B[dv] + // bucket, so erase its entry using iterator + // in O(1) + if (dv != std::numeric_limits::max()) { + auto findIter = std::find(B[dv].begin(), B[dv].end(), dist[v].second); + B[dv].erase(findIter); + } + + // updating the distance + dist[v].first = du + weight; + dv = dist[v].first; + + // pushing vertex v into updated distance's bucket + B[dv].push_front(v); + + // storing updated iterator in dist[v].second + dist[v].second = *(B[dv].begin()); + } + } + } + for (const auto &dist_i : dist) { + result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; + } + result.success = true; + + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_DIAL_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp new file mode 100644 index 000000000..206991e3e --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp @@ -0,0 +1,152 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_DIJKSTRA_IMPL_H__ +#define __CXXGRAPH_DIJKSTRA_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +const DijkstraResult Graph::dijkstra(const Node &source, + const Node &target) const { + DijkstraResult result; + auto nodeSet = Graph::getNodeSet(); + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // n denotes the number of vertices in graph + auto n = cachedAdjMatrix->size(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist; + + for (const auto &node : nodeSet) { + dist[node] = INF_DOUBLE; + } + + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue>>, + std::vector>>>, + std::greater>>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + pq.push(std::make_pair(0.0, *source_node_it)); + + // marking the distance of source as 0 + dist[*source_node_it] = 0; + + std::unordered_map parent; + parent[source.getUserId()] = ""; + + while (!pq.empty()) { + // second element of pair denotes the node / vertex + shared> currentNode = pq.top().second; + // first element of pair denotes the distance + double currentDist = pq.top().first; + + pq.pop(); + + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + if (elem.second->isDirected().has_value() && + elem.second->isDirected().value()) { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (dw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + dw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); + } + } else if (elem.second->isDirected().has_value() && + !elem.second->isDirected().value()) { + shared> udw_edge = + std::static_pointer_cast>( + elem.second); + if (udw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + udw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); + } + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + } + } + if (dist[*target_node_it] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.result = dist[*target_node_it]; + std::string id = target.getUserId(); + while (parent[id] != "") { + result.path.push_back(id); + id = parent[id]; + } + result.path.push_back(source.getUserId()); + std::reverse(result.path.begin(), result.path.end()); + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_DIJKSTRA_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp b/include/CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp new file mode 100644 index 000000000..02aebf06d --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp @@ -0,0 +1,108 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_FLOYDWARSHALL_IMPL_H__ +#define __CXXGRAPH_FLOYDWARSHALL_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +const FWResult Graph::floydWarshall() const { + FWResult result; + result.success = false; + result.errorMessage = ""; + std::unordered_map, double, + CXXGraph::pair_hash> + pairwise_dist; + const auto &nodeSet = Graph::getNodeSet(); + // create a pairwise distance matrix with distance node distances + // set to inf. Distance of node to itself is set as 0. + for (const auto &elem1 : nodeSet) { + for (const auto &elem2 : nodeSet) { + auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); + if (elem1 != elem2) + pairwise_dist[key] = INF_DOUBLE; + else + pairwise_dist[key] = 0.0; + } + } + + const auto &edgeSet = Graph::getEdgeSet(); + // update the weights of nodesfloydWarshall + // connected by edges + for (const auto &edge : edgeSet) { + const auto &elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edgeWeight = + (std::dynamic_pointer_cast(edge))->getWeight(); + auto key = + std::make_pair(elem.first->getUserId(), elem.second->getUserId()); + pairwise_dist[key] = edgeWeight; + if (edge->isDirected() == false) { + auto reverseKey = + std::make_pair(elem.second->getUserId(), elem.first->getUserId()); + pairwise_dist[reverseKey] = edgeWeight; + } + } else { + // if an edge exists but has no weight associated + // with it, we return an error message + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + for (const auto &k : nodeSet) { + // set all vertices as source one by one + for (const auto &src : nodeSet) { + // iterate through all vertices as destination for the + // current source + auto src_k = std::make_pair(src->getUserId(), k->getUserId()); + for (const auto &dst : nodeSet) { + // If vertex k provides a shorter path than + // src to dst, update the value of + // pairwise_dist[src_to_dst] + auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); + auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); + if (pairwise_dist[src_dst] > + (pairwise_dist[src_k] + pairwise_dist[k_dst]) && + (pairwise_dist[k_dst] != INF_DOUBLE && + pairwise_dist[src_k] != INF_DOUBLE)) + pairwise_dist[src_dst] = pairwise_dist[src_k] + pairwise_dist[k_dst]; + } + } + } + + result.success = true; + // presense of negative number in the diagonal indicates + // that that the graph contains a negative cycle + for (const auto &node : nodeSet) { + auto diag = std::make_pair(node->getUserId(), node->getUserId()); + if (pairwise_dist[diag] < 0.) { + result.negativeCycle = true; + return result; + } + } + result.result = std::move(pairwise_dist); + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_FLOYDWARSHALL_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp b/include/CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp new file mode 100644 index 000000000..1a1ad3efc --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp @@ -0,0 +1,108 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_FORDFULKERSON_IMPL_H__ +#define __CXXGRAPH_FORDFULKERSON_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +double Graph::fordFulkersonMaxFlow(const Node &source, + const Node &target) const { + if (!isDirectedGraph()) { + return -1; + } + double maxFlow = 0; + std::unordered_map>, shared>, nodeHash> + parent; + std::unordered_map< + shared>, + std::unordered_map>, double, nodeHash>, + nodeHash> + weightMap; + // build weight map + auto edgeSet = this->getEdgeSet(); + for (const auto &edge : edgeSet) { + // The Edge are all Directed at this point because is checked at the + // start + if (edge->isWeighted().value_or(false)) { + shared> dw_edge = + std::static_pointer_cast>(edge); + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + dw_edge->getWeight(); + } else { + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + 0; // No Weighted Edge are assumed to be 0 weigthed + } + } + + // Constuct iterators for source and target nodes in nodeSet + auto nodeSet = getNodeSet(); + auto source_node_ptr = *std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + auto target_node_ptr = *std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + + auto bfs_helper = [this, &source_node_ptr, &target_node_ptr, &parent, + &weightMap]() -> bool { + std::unordered_map>, bool, nodeHash> visited; + std::queue>> queue; + queue.push(source_node_ptr); + visited[source_node_ptr] = true; + parent[source_node_ptr] = nullptr; + while (!queue.empty()) { + auto u = queue.front(); + queue.pop(); + for (auto &v : weightMap[u]) { + if (!visited[v.first] && v.second > 0) { + queue.push(v.first); + visited[v.first] = true; + parent[v.first] = u; + } + } + } + + return (visited[target_node_ptr]); + }; + // Updating the residual values of edges + while (bfs_helper()) { + double pathFlow = std::numeric_limits::max(); + for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { + auto u = parent[v]; + pathFlow = std::min(pathFlow, weightMap[u][v]); + } + for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { + auto u = parent[v]; + weightMap[u][v] -= pathFlow; + weightMap[v][u] += pathFlow; + } + // Adding the path flows + maxFlow += pathFlow; + } + + return maxFlow; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_FORDFULKERSON_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Kahn_impl.hpp b/include/CXXGraph/Graph/Algorithm/Kahn_impl.hpp new file mode 100644 index 000000000..3194f8458 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Kahn_impl.hpp @@ -0,0 +1,86 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_KAHN_IMPL_H__ +#define __CXXGRAPH_KAHN_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +TopoSortResult Graph::kahn() const { + TopoSortResult result; + + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + const auto nodeSet = Graph::getNodeSet(); + result.nodesInTopoOrder.reserve(cachedAdjMatrix->size()); + + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + for (const auto &list : *cachedAdjMatrix) { + auto children = list.second; + for (const auto &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } + + std::queue>> topologicalOrder; + + for (const auto &node : nodeSet) { + if (!indegree[node->getId()]) { + topologicalOrder.emplace(node); + } + } + + size_t visited = 0; + while (!topologicalOrder.empty()) { + shared> currentNode = topologicalOrder.front(); + topologicalOrder.pop(); + result.nodesInTopoOrder.push_back(*currentNode); + + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &child : cachedAdjMatrix->at(currentNode)) { + if (--indegree[std::get<0>(child)->getId()] == 0) { + topologicalOrder.emplace(std::get<0>(child)); + } + } + } + visited++; + } + + if (visited != nodeSet.size()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + result.nodesInTopoOrder.clear(); + return result; + } + + result.success = true; + return result; + } +} +} // namespace CXXGraph +#endif // __CXXGRAPH_KAHN_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp b/include/CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp new file mode 100644 index 000000000..6e28acd7a --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp @@ -0,0 +1,127 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_KOSARAJU_IMPL_H__ +#define __CXXGRAPH_KOSARAJU_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" +#include + +namespace CXXGraph { + +template +SCCResult Graph::kosaraju() const { + SCCResult result; + result.success = false; + + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + auto nodeSet = getNodeSet(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + + std::stack>> st; + std::function>)> dfs_helper = + [this, &visited, &dfs_helper, &st](shared> source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + + st.push(source); + }; + + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + dfs_helper(node); + } + } + + // construct the transpose of the given graph + AdjacencyMatrix rev; + auto addElementToAdjMatrix = [&rev](shared> nodeFrom, + shared> nodeTo, + shared> edge) { + std::pair>, shared>> elem = {nodeTo, + edge}; + rev[nodeFrom].push_back(std::move(elem)); + }; + for (const auto &edgeSetIt : edgeSet) { + shared> d_edge = + std::static_pointer_cast>(edgeSetIt); + // Add the reverse edge to the reverse adjacency matrix + addElementToAdjMatrix(d_edge->getNodePair().second, + d_edge->getNodePair().first, d_edge); + } + + visited.clear(); + + std::function>, SCCResult, int)> dfs_helper1 = + [this, &rev, &visited, &dfs_helper1]( + shared> source, SCCResult result, int sccLabel) { + // mark the vertex visited + visited[source->getId()] = true; + // Add the current vertex to the strongly connected + // component + // comp.push_back(*source); + result.sccMap[source->getId()] = sccLabel; + + // travel the neighbors + for (int i = 0; i < rev[source].size(); i++) { + shared> neighbor = rev[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper1(neighbor, result, sccLabel); + } + } + }; + + int sccLabel = 0; + while (st.size() != 0) { + auto rem = st.top(); + st.pop(); + if (visited[rem->getId()] == false) { + // std::vector> comp; + dfs_helper1(rem, result, sccLabel); + sccLabel++; + // result.stronglyConnectedComps.push_back(comp); + } + } + result.noOfComponents = sccLabel; + result.success = true; + return result; + } +} +} // namespace CXXGraph +#endif // __CXXGRAPH_KOSARAJU_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Kruskal_impl.hpp b/include/CXXGraph/Graph/Algorithm/Kruskal_impl.hpp new file mode 100644 index 000000000..39e69096f --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Kruskal_impl.hpp @@ -0,0 +1,85 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_KRUSKAL_IMPL_H__ +#define __CXXGRAPH_KRUSKAL_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const MstResult Graph::kruskal() const { + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + const auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + auto edgeSet = Graph::getEdgeSet(); + std::priority_queue>>, + std::vector>>>, + std::greater>>>> + sortedEdges; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto weight = + (std::dynamic_pointer_cast(edge))->getWeight(); + sortedEdges.push(std::make_pair(weight, edge)); + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + auto subset = make_shared>(); + + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + (*subset)[node->getId()] = set; + } + result.mstCost = 0; + while ((!sortedEdges.empty()) && (result.mst.size() < n)) { + auto [edgeWeight, cheapestEdge] = sortedEdges.top(); + sortedEdges.pop(); + auto &[first, second] = cheapestEdge->getNodePair(); + auto set1 = Graph::setFind(subset, first->getId()); + auto set2 = Graph::setFind(subset, second->getId()); + if (set1 != set2) { + result.mst.push_back( + std::make_pair(first->getUserId(), second->getUserId())); + result.mstCost += edgeWeight; + } + Graph::setUnion(subset, set1, set2); + } + result.success = true; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_KRUSKAL_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Prim_impl.hpp b/include/CXXGraph/Graph/Algorithm/Prim_impl.hpp new file mode 100644 index 000000000..c6499eae5 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Prim_impl.hpp @@ -0,0 +1,112 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_PRIM_IMPL_H__ +#define __CXXGRAPH_PRIM_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const MstResult Graph::prim() const { + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + if (!isConnectedGraph()) { + result.errorMessage = ERR_NOT_STRONG_CONNECTED; + return result; + } + auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist; + for (const auto &elem : (*cachedAdjMatrix)) { + dist[elem.first] = INF_DOUBLE; + } + + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue>>, + std::vector>>>, + std::greater>>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + auto source = *(nodeSet.begin()); + pq.push(std::make_pair(0.0, source)); + result.mstCost = 0; + std::vector doneNode; + // mark source node as done + // otherwise we get (0, 0) also in mst + doneNode.push_back(source->getId()); + // stores the parent and corresponding child node + // of the edges that are part of MST + std::unordered_map parentNode; + while (!pq.empty()) { + // second element of pair denotes the node / vertex + shared> currentNode = pq.top().second; + auto nodeId = currentNode->getId(); + if (std::find(doneNode.begin(), doneNode.end(), nodeId) == doneNode.end()) { + auto pair = std::make_pair(parentNode[nodeId], currentNode->getUserId()); + result.mst.push_back(pair); + result.mstCost += pq.top().first; + doneNode.push_back(nodeId); + } + + pq.pop(); + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + shared> udw_edge = + std::static_pointer_cast>( + elem.second); + if ((udw_edge->getWeight() < dist[elem.first]) && + (std::find(doneNode.begin(), doneNode.end(), + elem.first->getId()) == doneNode.end())) { + dist[elem.first] = udw_edge->getWeight(); + parentNode[elem.first->getId()] = currentNode->getUserId(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + } + } + result.success = true; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_PRIM_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Tarjan_impl.hpp b/include/CXXGraph/Graph/Algorithm/Tarjan_impl.hpp new file mode 100644 index 000000000..a2fd2ec6b --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/Tarjan_impl.hpp @@ -0,0 +1,198 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_TARJAN_IMPL_H__ +#define __CXXGRAPH_TARJAN_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const TarjanResult Graph::tarjan(const unsigned int typeMask) const { + TarjanResult result; + result.success = false; + bool isDirected = this->isDirectedGraph(); + if (isDirected) { + // check whether targetMask is a subset of the mask for directed graph + unsigned int directedMask = TARJAN_FIND_SCC; + if ((typeMask | directedMask) != directedMask) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + } else { + // check whether targetMask is a subset of the mask for undirected graph + unsigned int undirectedMask = (TARJAN_FIND_CUTV | TARJAN_FIND_BRIDGE | + TARJAN_FIND_VBCC | TARJAN_FIND_EBCC); + if ((typeMask | undirectedMask) != undirectedMask) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } + } + + const auto &nodeSet = getNodeSet(); + std::unordered_map + discoveryTime; // the timestamp when a node is visited + std::unordered_map + lowestDisc; // the lowest discovery time of all + // reachable nodes from current node + int timestamp = 0; + CXXGraph::id_t rootId = 0; + std::stack> sccNodeStack; + std::stack> ebccNodeStack; + std::stack> vbccNodeStack; + std::unordered_set inStack; + std::function>, const shared>)> + dfs_helper = [this, typeMask, isDirected, &dfs_helper, &discoveryTime, + &lowestDisc, ×tamp, &rootId, &sccNodeStack, + &ebccNodeStack, &vbccNodeStack, &inStack, + &result](const shared> curNode, + const shared> prevEdge) { + // record the visited time of current node + discoveryTime[curNode->getId()] = timestamp; + lowestDisc[curNode->getId()] = timestamp; + timestamp++; + if (typeMask & TARJAN_FIND_SCC) { + sccNodeStack.emplace(*curNode); + inStack.emplace(curNode->getId()); + } + if (typeMask & TARJAN_FIND_EBCC) { + ebccNodeStack.emplace(*curNode); + } + if (typeMask & TARJAN_FIND_VBCC) { + vbccNodeStack.emplace(*curNode); + } + // travel the neighbors + if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { + int numSon = 0; + bool nodeIsAdded = + false; // whether a node has been marked as a cut vertice + for (const auto &[neighborNode, edge] : + cachedAdjMatrix->at(curNode)) { + if (!discoveryTime.count(neighborNode->getId())) { + dfs_helper(neighborNode, edge); + lowestDisc[curNode->getId()] = + std::min(lowestDisc[curNode->getId()], + lowestDisc[neighborNode->getId()]); + + if (typeMask & TARJAN_FIND_BRIDGE) { + // lowestDisc of neighbor node is larger than that of current + // node means we can travel back to a visited node only through + // this edge + if (discoveryTime[curNode->getId()] < + lowestDisc[neighborNode->getId()]) { + result.bridges.emplace_back(*edge); + } + } + + if ((typeMask & TARJAN_FIND_CUTV) && (nodeIsAdded == false)) { + if (curNode->getId() == rootId) { + numSon++; + // a root node is a cut vertices only when it connects at + // least two connected components + if (numSon == 2) { + nodeIsAdded = true; + result.cutVertices.emplace_back(*curNode); + } + } else { + if (discoveryTime[curNode->getId()] <= + lowestDisc[neighborNode->getId()]) { + nodeIsAdded = true; + result.cutVertices.emplace_back(*curNode); + } + } + } + + if (typeMask & TARJAN_FIND_VBCC) { + if (discoveryTime[curNode->getId()] <= + lowestDisc[neighborNode->getId()]) { + // if current node is a cut vertice or the root node, the vbcc + // a vertice-biconnect-component which contains the neighbor + // node + std::vector> vbcc; + while (true) { + // pop a top node out of stack until + // the neighbor node has been poped out + Node nodeAtTop = vbccNodeStack.top(); + vbccNodeStack.pop(); + vbcc.emplace_back(nodeAtTop); + if (nodeAtTop == *neighborNode) { + break; + } + } + vbcc.emplace_back(*curNode); + result.verticeBiconnectedComps.emplace_back(std::move(vbcc)); + } + } + } else if ((edge != prevEdge) && + ((isDirected == false) || + (inStack.count(neighborNode->getId())))) { + // it's not allowed to go through the previous edge back + // for a directed graph, it's also not allowed to visit + // a node that is not in stack + lowestDisc[curNode->getId()] = + std::min(lowestDisc[curNode->getId()], + lowestDisc[neighborNode->getId()]); + } + } + } + // find sccs for a undirected graph is very similar with + // find ebccs for a directed graph + if ((typeMask & TARJAN_FIND_SCC) || (typeMask & TARJAN_FIND_EBCC)) { + std::stack> &nodeStack = + (typeMask & TARJAN_FIND_SCC) ? sccNodeStack : ebccNodeStack; + if (discoveryTime[curNode->getId()] == lowestDisc[curNode->getId()]) { + std::vector> connectedComp; + while (true) { + // pop a top node out of stack until + // the current node has been poped out + Node nodeAtTop = nodeStack.top(); + nodeStack.pop(); + if (typeMask & TARJAN_FIND_SCC) { + inStack.erase(nodeAtTop.getId()); + } + connectedComp.emplace_back(nodeAtTop); + if (nodeAtTop == *curNode) { + break; + } + } + // store this component in result + (typeMask & TARJAN_FIND_SCC) + ? result.stronglyConnectedComps.emplace_back( + std::move(connectedComp)) + : result.edgeBiconnectedComps.emplace_back( + std::move(connectedComp)); + } + } + }; + + for (const auto &node : nodeSet) { + if (!discoveryTime.count(node->getId())) { + rootId = node->getId(); + dfs_helper(node, nullptr); + } + } + + result.success = true; + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_TARJAN_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp b/include/CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp new file mode 100644 index 000000000..83e293fe6 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp @@ -0,0 +1,78 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_TOPOLOGICALSORT_IMPL_H__ +#define __CXXGRAPH_TOPOLOGICALSORT_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +TopoSortResult Graph::topologicalSort() const { + TopoSortResult result; + result.success = false; + + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else if (isCyclicDirectedGraphBFS()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + return result; + } else { + const auto &nodeSet = getNodeSet(); + std::unordered_map>, bool, nodeHash> visited; + + std::function>)> postorder_helper = + [this, &postorder_helper, &visited, + &result](shared> curNode) { + visited[curNode] = true; + + if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { + for (const auto &edge : cachedAdjMatrix->at(curNode)) { + const auto &nextNode = edge.first; + if (false == visited[nextNode]) { + postorder_helper(nextNode); + } + } + } + + result.nodesInTopoOrder.push_back(*curNode); + }; + + auto numNodes = cachedAdjMatrix->size(); + result.nodesInTopoOrder.reserve(numNodes); + + for (const auto &node : nodeSet) { + if (false == visited[node]) { + postorder_helper(node); + } + } + + result.success = true; + std::reverse(result.nodesInTopoOrder.begin(), + result.nodesInTopoOrder.end()); + return result; + } +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_TOPOLOGICALSORT_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp b/include/CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp new file mode 100644 index 000000000..942637450 --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp @@ -0,0 +1,61 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_TRANSITIVEREDUCTION_IMPL_H__ +#define __CXXGRAPH_TRANSITIVEREDUCTION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +/* + * See Harry Hsu. "An algorithm for finding a minimal equivalent graph of a + * digraph.", Journal of the ACM, 22(1):11-16, January 1975 + * + * foreach x in graph.vertices + * foreach y in graph.vertices + * foreach z in graph.vertices + * delete edge xz if edges xy and yz exist + */ +template +const Graph Graph::transitiveReduction() const { + Graph result(this->edgeSet); + + CXXGraph::id_t edgeId = 0; + std::unordered_set>, nodeHash> nodes = + this->getNodeSet(); + for (auto x : nodes) { + for (auto y : nodes) { + if (this->findEdge(x, y, edgeId)) { + for (auto z : nodes) { + if (this->findEdge(y, z, edgeId)) { + if (this->findEdge(x, z, edgeId)) { + result.removeEdge(edgeId); + } + } + } + } + } + } + + return result; +} +} // namespace CXXGraph +#endif // __CXXGRAPH_TRANSITIVEREDUCTION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Graph.h b/include/CXXGraph/Graph/Graph.h new file mode 100644 index 000000000..4691f67e6 --- /dev/null +++ b/include/CXXGraph/Graph/Graph.h @@ -0,0 +1,50 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_GRAPH_H__ +#define __CXXGRAPH_GRAPH_H__ + +#include "CXXGraph/Graph/Graph_impl.hpp" + +//Algorithm +#include "CXXGraph/Graph/Algorithm/BellmanFord_impl.hpp" +#include "CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Boruvka_impl.hpp" +#include "CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Connectivity_impl.hpp" +#include "CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp" +#include "CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Dial_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp" +#include "CXXGraph/Graph/Algorithm/FloydWarshall_impl.hpp" +#include "CXXGraph/Graph/Algorithm/FordFulkerson_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Kahn_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Kosaraju_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Kruskal_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Prim_impl.hpp" +#include "CXXGraph/Graph/Algorithm/Tarjan_impl.hpp" +#include "CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp" +#include "CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp" + +// IO Operation +#include "CXXGraph/Graph/IO/IOUtility_impl.hpp" +#include "CXXGraph/Graph/IO/InputOperation_impl.hpp" +#include "CXXGraph/Graph/IO/OutputOperation_impl.hpp" + +#endif // __CXXGRAPH_GRAPH_H__ diff --git a/include/CXXGraph/Graph/Graph.hpp b/include/CXXGraph/Graph/Graph.hpp deleted file mode 100644 index c4a9a5159..000000000 --- a/include/CXXGraph/Graph/Graph.hpp +++ /dev/null @@ -1,4266 +0,0 @@ -/***********************************************************/ -/*** ______ ____ ______ _ ***/ -/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ -/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ -/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ -/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ -/*** |_| ***/ -/***********************************************************/ -/*** Header-Only C++ Library for Graph ***/ -/*** Representation and Algorithms ***/ -/***********************************************************/ -/*** Author: ZigRazor ***/ -/*** E-Mail: zigrazor@gmail.com ***/ -/***********************************************************/ -/*** Collaboration: ----------- ***/ -/***********************************************************/ -/*** License: AGPL v3.0 ***/ -/***********************************************************/ - -#ifndef __CXXGRAPH_GRAPH_H__ -#define __CXXGRAPH_GRAPH_H__ - -#include -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Node/Node.hpp" -#include "CXXGraph/Partitioning/Partition.hpp" -#include "CXXGraph/Partitioning/PartitionAlgorithm.hpp" -#include "CXXGraph/Partitioning/Partitioner.hpp" -#include "CXXGraph/Partitioning/Utility/Globals.hpp" -#include "CXXGraph/Utility/ConstString.hpp" -#include "CXXGraph/Utility/ConstValue.hpp" -#include "CXXGraph/Utility/PointerHash.hpp" -#include "CXXGraph/Utility/Reader.hpp" -#include "CXXGraph/Utility/ThreadSafe.hpp" -#include "CXXGraph/Utility/TypeTraits.hpp" -#include "CXXGraph/Utility/Typedef.hpp" -#include "CXXGraph/Utility/Writer.hpp" - -#ifdef WITH_COMPRESSION -#include -#endif - -namespace CXXGraph { -// Smart pointers alias -template -using unique = std::unique_ptr; -template -using shared = std::shared_ptr; - -using std::make_shared; -using std::make_unique; - -template -using T_EdgeSet = std::unordered_set>, edgeHash>; - -template -using T_NodeSet = std::unordered_set>, nodeHash>; - -namespace Partitioning { -template -class Partition; -} - -template -std::ostream &operator<<(std::ostream &o, const Graph &graph); -template -std::ostream &operator<<(std::ostream &o, const AdjacencyMatrix &adj); - -/// Class that implement the Graph. ( This class is not Thread Safe ) -template -class Graph { - private: - T_EdgeSet edgeSet = {}; - T_NodeSet isolatedNodesSet = {}; - - shared> cachedAdjMatrix; - shared> cachedDegreeMatrix; - shared> cachedLaplacianMatrix; - shared> cachedTransitionMatrix; - // Private non-const getter for the set of nodes - std::unordered_set>, nodeHash> nodeSet(); - - std::optional> getExtenstionAndSeparator( - InputOutputFormat format) const; - void writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, - std::ostream &oEdgeWeight, const char &sep, - bool writeNodeFeat, bool writeEdgeWeight) const; - void readGraphFromStream(std::istream &iGraph, std::istream &iNodeFeat, - std::istream &iEdgeWeight, bool readNodeFeat, - bool readEdgeWeight); - int writeToDot(const std::string &workingDir, const std::string &OFileName, - const std::string &graphName) const; - int readFromDot(const std::string &workingDir, const std::string &fileName); - void recreateGraph( - std::unordered_map> - &edgeMap, - std::unordered_map &edgeDirectedMap, - std::unordered_map &nodeFeatMap, - std::unordered_map &edgeWeightMap); - -#ifdef WITH_COMPRESSION - int compressFile(const std::string &inputFile, - const std::string &outputFile) const; - int decompressFile(const std::string &inputFile, - const std::string &outputFile) const; -#endif - - public: - Graph(); - Graph(const T_EdgeSet &edgeSet); - virtual ~Graph() = default; - /** - * \brief - * Function that return the Edge set of the Graph - * Note: No Thread Safe - * - * @returns a list of Edges of the graph - * - */ - virtual const T_EdgeSet &getEdgeSet() const; - /** - * \brief - * Function set the Edge Set of the Graph - * Note: No Thread Safe - * - * @param edgeSet The Edge Set - * - */ - virtual void setEdgeSet(const T_EdgeSet &edgeSet); - /** - * \brief - * Function add an Edge to the Graph Edge Set - * First check if a pointer to a node with the same userId has - * already been added, and if not add it - * Note: No Thread Safe - * - * @param edge The Edge to insert - * - */ - virtual void addEdge(const Edge *edge); - /** - * \brief - * Function add an Edge to the Graph Edge Set - * First check if a pointer to a node with the same userId has - * already been added, and if not add it - * Note: No Thread Safe - * - * @param edge The Edge to insert - * - */ - virtual void addEdge(shared> edge); - /** - * \brief - * Function that adds any number of Edges to the Graph Edge set - * Note: This is the overload needed to terminate the - * recursion - * - * @param None - * - */ - template - void addEdges(); - /** - * \brief - * Function that adds any number of Edges to the Graph Edge set - * - * @param Raw pointers or shared pointers to the Edges - * - */ - template - std::enable_if && (is_edge_ptr_v && ...), void> - addEdges(T1 edge, Tn... edges); - /** - * \brief - * Function to add a Node to the Graph Node Set - * Note: No Thread Safe - * - * @param pointer to the node - * - */ - virtual void addNode(const Node *node); - /** - * \brief - * Function to add a Node to the Graph Node Set - * Note: No Thread Safe - * - * @param shared pointer to the node - * - */ - virtual void addNode(shared> node); - /** - * \brief - * Function that adds any number of Nodes to the Graph Node set - * Note: This overload is needed to terminate the recursion - * - * @param None - * - */ - template - void addNodes(); - /** - * \brief - * Function that adds any number of Nodes to the Graph Node set - * - * @param Raw pointers or shared pointers to the Edges - * - */ - template - std::enable_if && (is_node_ptr_v && ...), void> - addNodes(T1 node, Tn... nodes); - /** - * \brief - * Function remove an Edge from the Graph Edge Set - * Note: No Thread Safe - * - * @param edgeId The Edge Id to remove - * - */ - virtual void removeEdge(const CXXGraph::id_t edgeId); - /** - * \brief - * Function to remove a Node from the Graph Node Set - * Note: No Thread Safe - * - * @param edgeId The Edge Id to remove - * - */ - virtual void removeNode(const std::string &nodeUserId); - /** - * \brief - * Finds the given edge defined by v1 and v2 within the graph. - * - * @param v1 The first vertex. - * @param v2 The second vertex. - * @param id The edge id if the edge is found. Otherwise set to 0. - * @return True if the edge exists in the graph. - */ - virtual bool findEdge(const Node *v1, const Node *v2, - CXXGraph::id_t &id) const; - /** - * \brief - * Overload of findEdge which takes shared pointers as parameters - * - * @param v1 The first vertex. - * @param v2 The second vertex. - * @param id The edge id if the edge is found. Otherwise set to 0. - * @return True if the edge exists in the graph. - */ - virtual bool findEdge(shared> v1, shared> v2, - CXXGraph::id_t &id) const; - /** - * \brief - * Function that return the Node Set of the Graph - * Note: No Thread Safe - * - * @returns a list of Nodes of the graph - * - */ - virtual const T_NodeSet getNodeSet() const; - /** - * \brief - * Function that return the Set of isolated nodes - * in the Graph - * Note: No Thread Safe - * - * @returns a list of Nodes of the graph - * - */ - virtual const T_NodeSet getIsolatedNodeSet() const; - /** - * \brief - * Function that sets the data contained in a node - * - * @param nodeUserId The userId string of the node whose data is to be changes - * @param data The new value for the node data - * - */ - virtual void setNodeData(const std::string &nodeUserId, T data); - /** - * \brief - * Function that sets the data contained in every node of the graph - * - * @param dataMap Map of the userId of every node with its new data value - * - */ - virtual void setNodeData(std::map &dataMap); - /** - * \brief - * Function that return an Edge with specific ID if Exist in the Graph - * Note: No Thread Safe - * - * @param edgeId The Edge Id to return - * @returns the Edge if exist - * - */ - virtual const std::optional>> getEdge( - const CXXGraph::id_t edgeId) const; - /** - * \brief - * Function that return a Node with specific ID if Exist in the Graph - * Note: No Thread Safe - * - * @param nodeId The Node Id to return - * @returns the Node if exist - * - */ - virtual const std::optional>> getNode( - const std::string &nodeUserId) const; - /** - * @brief This function generate a list of adjacency matrix with every element - * of the matrix contain the node where is directed the link and the Edge - * corrispondent to the link - * Note: No Thread Safe - */ - virtual shared> getAdjMatrix() const; - - virtual void cacheAdjMatrix(); - /** - * @brief This function generates a list of the degree matrix with every - * element of the matrix containing the node where the link is directed and - * the corresponding edge to the link. Note: No Thread Safe - */ - virtual shared> getDegreeMatrix() const; - - virtual void cacheDegreeMatrix(); - /** - * @brief This function generates a list of the Laplacian matrix with every - * element of the matrix containing the node connected to the current node and - * the corresponding edge to the link. Note: No Thread Safe - */ - virtual shared> getLaplacianMatrix() const; - - virtual void cacheLaplacianMatrix(); - /** - * @brief This function generates a list of the transition matrix with every - * element of the matrix containing the node that can be transitioned to from - * the current node and the probability of the transition. Note: No Thread - * Safe - */ - virtual shared> getTransitionMatrix() const; - - virtual void cacheTransitionMatrix(); - /** - * \brief This function generates a set of nodes linked to the provided node - * in a directed graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - outNeighbors(const Node *node) const; - /** - * \brief This function generates a set of nodes linked to the provided node - * in a directed graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - outNeighbors(shared> node) const; - /** - * \brief This function generates a set of nodes linked to the provided node - * in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - inOutNeighbors(const Node *node) const; - /** - * \brief - * \brief This function generates a set of nodes linked to the provided node - * in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, nodeHash> - inOutNeighbors(shared> node) const; - /** - * \brief - * \brief This function generates a set of Edges going out of a node - * in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> outEdges( - const Node *node) const; - /** - * \brief - * \brief This function generates a set of Edges going out of a node - * in any graph - * - * @param Shared pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> outEdges( - shared> node) const; - /** - * \brief - * \brief This function generates a set of Edges coming in or going out of - * a node in any graph - * - * @param Pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> - inOutEdges(const Node *node) const; - /** - * \brief - * \brief This function generates a set of Edges coming in or going out of - * a node in any graph - * - * @param Shared pointer to the node - * - */ - virtual const std::unordered_set>, edgeHash> - inOutEdges(shared> node) const; - /** - * @brief This function finds the subset of given a nodeId - * Subset is stored in a map where keys are the hash-id of the node & values - * is the subset. - * @param subset query subset, we want to find target in this subset - * @param elem elem that we wish to find in the subset - * - * @return parent node of elem - * Note: No Thread Safe - */ - virtual CXXGraph::id_t setFind(std::unordered_map *, - const CXXGraph::id_t elem) const; - /** - * @brief This function finds the subset of given a nodeId - * Subset is stored in a map where keys are the hash-id of the node & values - * is the subset. - * @param shared pointer to subset query subset, we want to find target in - * this subset - * @param elem elem that we wish to find in the subset - * - * @return parent node of elem - * Note: No Thread Safe - */ - virtual CXXGraph::id_t setFind( - shared>, - const CXXGraph::id_t elem) const; - /** - * @brief This function modifies the original subset array - * such that it the union of two sets a and b - * @param subset original subset is modified to obtain union of a & b - * @param a parent id of set1 - * @param b parent id of set2 - * NOTE: Original subset is no longer available after union. - * Note: No Thread Safe - */ - virtual void setUnion(std::unordered_map *, - const CXXGraph::id_t set1, - const CXXGraph::id_t elem2) const; - /** - * @brief This function modifies the original subset array - * such that it the union of two sets a and b - * @param subset original subset is modified to obtain union of a & b - * @param a parent id of set1 - * @param b parent id of set2 - * NOTE: Original subset is no longer available after union. - * Note: No Thread Safe - */ - virtual void setUnion(shared>, - const CXXGraph::id_t set1, - const CXXGraph::id_t elem2) const; - /** - * @brief This function finds the eulerian path of a directed graph using - * hierholzers algorithm - * - * @return a vector containing nodes in eulerian path - * Note: No Thread Safe - */ - virtual std::shared_ptr>> eulerianPath() const; - /** - * @brief Function runs the dijkstra algorithm for some source node and - * target node in the graph and returns the shortest distance of target - * from the source. - * Note: No Thread Safe - * - * @param source source vertex - * @param target target vertex - * - * @return shortest distance if target is reachable from source else ERROR in - * case if target is not reachable from source or there is error in the - * computation. - */ - virtual const DijkstraResult dijkstra(const Node &source, - const Node &target) const; - /** - * @brief This function runs the tarjan algorithm and returns different types - * of results depending on the input parameter typeMask. - * - * @param typeMask each bit of typeMask within valid range represents a kind - * of results should be returned. - * - * Note: No Thread Safe - * - * @return The types of return include strongly connected components - * (only for directed graphs) and cut vertices、 bridges、edge - * biconnected components and vertice biconnected components - * (only for undirected graphs). - */ - virtual const TarjanResult tarjan(const unsigned int typeMask) const; - /** - * @brief Function runs the bellman-ford algorithm for some source node and - * target node in the graph and returns the shortest distance of target - * from the source. It can also detect if a negative cycle exists in the - * graph. Note: No Thread Safe - * - * @param source source vertex - * @param target target vertex - * - * @return shortest distance if target is reachable from source else ERROR in - * case if target is not reachable from source. If there is no error then also - * returns if the graph contains a negative cycle. - */ - virtual const BellmanFordResult bellmanford(const Node &source, - const Node &target) const; - /** - * @brief This function computes the transitive reduction of the graph, - * returning a graph with the property of transitive closure satisfied. It - * removes the "short-circuit" paths from a graph, leaving only the longest - * paths. Commonly used to remove duplicate edges among nodes that do not pass - * through the entire graph. - * @return A copy of the current graph with the transitive closure property - * satisfied. - * - */ - virtual const Graph transitiveReduction() const; - /** - * @brief Function runs the floyd-warshall algorithm and returns the shortest - * distance of all pair of nodes. It can also detect if a negative cycle - * exists in the graph. Note: No Thread Safe - * @return a map whose keys are node ids and values are the shortest distance. - * If there is no error then also returns if the graph contains a negative - * cycle. - */ - virtual const FWResult floydWarshall() const; - /** - * @brief Function runs the prim algorithm and returns the minimum spanning - * tree if the graph is undirected. Note: No Thread Safe - * @return a vector containing id of nodes in minimum spanning tree & cost of - * MST - */ - virtual const MstResult prim() const; - /** - * @brief Function runs the boruvka algorithm and returns the minimum spanning - * tree & cost if the graph is undirected. Note: No Thread Safe - * @return struct of type MstResult with following fields - * success: true if algorithm completed successfully ELSE false - * mst: vector containing id of nodes in minimum spanning tree & cost of MST - * mstCost: Cost of MST - * errorMessage: "" if no error ELSE report the encountered error - */ - virtual const MstResult boruvka() const; - /** - * @brief Function runs the kruskal algorithm and returns the minimum spanning - * tree if the graph is undirected. Note: No Thread Safe - * @return struct of type MstResult with following fields - * success: true if algorithm completed successfully ELSE false - * mst: vector containing id of nodes in minimum spanning tree & cost of MST - * mstCost: Cost of MST - * errorMessage: "" if no error ELSE report the encountered error - */ - virtual const MstResult kruskal() const; - /** - * \brief - * Function runs the best first search algorithm over the graph - * using an evaluation function to decide which adjacent node is - * most promising to explore - * Note: No Thread Safe - * - * @param source source node - * @param target target node - * @returns a struct with a vector of Nodes if target is reachable else ERROR - * in case if target is not reachable or there is an error in the computation. - * - */ - virtual BestFirstSearchResult best_first_search( - const Node &source, const Node &target) const; - /** - * \brief - * Function performs the breadth first search algorithm over the graph - * Note: No Thread Safe - * - * @param start Node from where traversing starts - * @returns a vector of Node indicating which Node were visited during the - * search. - * - */ - virtual const std::vector> breadth_first_search( - const Node &start) const; - /** - * \brief - * The multithreaded version of breadth_first_search - * It turns out to be two indepentent functions because of implemntation - * differences - * - * @param start Node from where traversing starts - * @param num_threads number of threads - * @returns a vector of Node indicating which Node were visited during the - * search. - * - */ - virtual const std::vector> concurrency_breadth_first_search( - const Node &start, size_t num_threads) const; - /** - * \brief - * Function performs the depth first search algorithm over the graph - * Note: No Thread Safe - * - * @param start Node from where traversing starts - * @returns a vector of Node indicating which Node were visited during the - * search. - * - */ - virtual const std::vector> depth_first_search( - const Node &start) const; - - /** - * \brief - * This function uses DFS to check for cycle in the graph. - * Pay Attention, this function work only with directed Graph - * Note: No Thread Safe - * - * @return true if a cycle is detected, else false. ( false is returned also - * if the graph in indirected) - */ - virtual bool isCyclicDirectedGraphDFS() const; - - /** - * \brief - * This function uses BFS to check for cycle in the graph. - * Pay Attention, this function work only with directed Graph - * Note: No Thread Safe - * - * @return true if a cycle is detected, else false. ( false is returned also - * if the graph in indirected) - */ - virtual bool isCyclicDirectedGraphBFS() const; - - /** - * @brief - * This function checks if the given set of edges - * forms a cycle or not using union-find method. - * - * @return true if a cycle is detected, else false - */ - virtual bool containsCycle(const T_EdgeSet *) const; - /** - * @brief - * This function checks if the given set of edges - * forms a cycle or not using union-find method. - * - * @return true if a cycle is detected, else false - */ - virtual bool containsCycle(shared>) const; - /** - * @brief - * This function checks if the given Subset - * forms a cycle or not using union-find method. - * - * @return true if a cycle is detected, else false - */ - virtual bool containsCycle( - shared> edgeSet, - shared>) const; - - /** - * \brief - * This function checks if a graph is directed - * Note: No Thread Safe - * - * @return true if the graph is directed, else false. - */ - virtual bool isDirectedGraph() const; - - /** - * \brief - * This function checks if a graph is undirected - * Note: No Thread Safe - * - * @return true if the graph is undirected, else false. - */ - virtual bool isUndirectedGraph() const; - - /** - * @brief This function reverse the direction of the edges in a directed graph - */ - virtual void reverseDirectedGraph(); - - /** - * @brief This function checks if the graph is connected or not - * Applicable for Undirected Graph, for Directed Graph use the - * isStronglyConnectedGraph() function - * - * @return true if the graph is connected - * @return false otherwise - */ - virtual bool isConnectedGraph() const; - - /** - * @brief This function checks if the graph is strongly connected or not - * Applicable for Directed Graph, for Undirected Graph use the - * isConnectedGraph() function - * - * @return true if the graph is connected - * @return false otherwise - */ - virtual bool isStronglyConnectedGraph() const; - - /** - * @brief This function sort nodes in topological order. - * Applicable for Directed Acyclic Graph - * - * @return a struct with a vector of Nodes ordered topologically else ERROR in - * case of undirected or cyclic graph - */ - virtual TopoSortResult topologicalSort() const; - - /** - * @brief This function sort nodes in topological order using kahn's algorithm - * Applicable for Directed Acyclic Graph - * - * @return a struct with a vector of Nodes ordered topologically else ERROR in - * case of undirected or cyclic graph - */ - virtual TopoSortResult kahn() const; - - /** - * \brief - * This function performs performs the kosaraju algorthm on the graph to find - the strongly connected components. - * - * Mathematical definition of the problem: - * A strongly connected component (SCC) of a directed graph is a maximal - strongly connected subgraph. - - * Note: No Thread Safe - * @return a vector of vector of strongly connected components. - */ - virtual SCCResult kosaraju() const; - - /** - * \brief - * This function performs Graph Slicing based on connectivity - * - * Mathematical definition of the problem: - * - * Let G be the set of nodes in a graph and n be a given node in that set. - * Let C be the non-strict subset of G containing both n and all nodes - reachable - * from n, and let C' be its complement. There's a third set M, which is the - * non-strict subset of C containing all nodes that are reachable from any node - in C'. - * The problem consists of finding all nodes that belong to C but not to M. - - * Note: No Thread Safe - * @param start Node from where traversing starts - * @return a vector of nodes that belong to C but not to M. - */ - virtual const std::vector> graph_slicing(const Node &start) const; - - /** - * @brief Function runs the Dial algorithm (Optimized Dijkstra for small - * range weights) for some source node and target node in the graph and - * returns the shortest distance of target from the source. Note: No Thread - * Safe - * - * @param source source vertex - * @param maxWeight maximum weight of the edge - * - * @return shortest distance for all nodes reachable from source else ERROR in - * case there is error in the computation. - */ - virtual const DialResult dial(const Node &source, int maxWeight) const; - - /** - * @brief Function runs the Ford-Fulkerson algorithm for some source node and - * target node in the graph and returns the maximum flow of the graph - * - * @param source source vertex - * @param target target vertex - * @return double Max-Flow value or -1 in case of error - */ - virtual double fordFulkersonMaxFlow(const Node &source, - const Node &target) const; - - /** - * \brief - * This function writes the graph to an output file - * Note: Not threadsafe - * - * @param format The output format of the file - * @param workingDir The parent directory of the output file - * @param OFileName The output filename - * @param compress Enables compression (requires zlib) - * @param writeNodeFeat Indicates if export also Node Features - * @param writeEdgeWeight Indicates if export also Edge Weights - * @return 0 if OK, else return a negative value - */ - virtual int writeToFile( - InputOutputFormat format = InputOutputFormat::STANDARD_CSV, - const std::string &workingDir = ".", - const std::string &OFileName = "graph", bool compress = false, - bool writeNodeFeat = false, bool writeEdgeWeight = false) const; - - virtual int writeToDotFile(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName) const; - - virtual int writeToMTXFile(const std::string &workingDir, - const std::string &OFileName, char delimier) const; - - /** - * \brief - * This function reads the graph from an input file - * Note: Not threadsafe - * - * @param format The input format of the file - * @param workingDir The parent directory of the input - * file - * @param OFileName The input filename - * @param compress Enables compression (requires zlib) - * @param readNodeFeat Indicates if import also Node Features - * @param readEdgeWeight Indicates if import also Edge Weights - * @return 0 if OK, else return a negative value - */ - virtual int readFromFile( - InputOutputFormat format = InputOutputFormat::STANDARD_CSV, - const std::string &workingDir = ".", - const std::string &OFileName = "graph", bool compress = false, - bool readNodeFeat = false, bool readEdgeWeight = false); - - virtual int readFromDotFile(const std::string &workingDir, - const std::string &fileName); - - virtual int readFromMTXFile(const std::string &workingDir, - const std::string &fileName); - - /** - * \brief - * This function partition a graph in a set of partitions - * Note: No Thread Safe - * - * @param algorithm The partition algorithm - * @param numberOfPartition The number of partitions - * @return The partiton Map of the partitioned graph - */ - virtual PartitionMap partitionGraph( - const Partitioning::PartitionAlgorithm algorithm, - const unsigned int numberOfPartitions, const double param1 = 0.0, - const double param2 = 0.0, const double param3 = 0.0, - const unsigned int numberOfthreads = - std::thread::hardware_concurrency()) const; - - friend std::ostream &operator<< <>(std::ostream &os, const Graph &graph); - friend std::ostream &operator<< <>(std::ostream &os, - const AdjacencyMatrix &adj); -}; - -template -Graph::Graph() { - /* Caching the adjacency matrix */ - cacheAdjMatrix(); - cacheDegreeMatrix(); - cacheLaplacianMatrix(); - cacheTransitionMatrix(); -} - -template -Graph::Graph(const T_EdgeSet &edgeSet) { - for (auto edgeIt : edgeSet) { - this->edgeSet.insert(edgeIt); - } - /* Caching the adjacency matrix */ - cacheAdjMatrix(); - cacheDegreeMatrix(); - cacheLaplacianMatrix(); - cacheTransitionMatrix(); -} - -template -const T_EdgeSet &Graph::getEdgeSet() const { - return edgeSet; -} - -template -void Graph::setEdgeSet(const T_EdgeSet &edgeSet) { - this->edgeSet.clear(); - for (auto edgeIt : edgeSet) { - this->edgeSet.insert(edgeIt); - } - /* Caching the adjacency matrix */ - cacheAdjMatrix(); - cacheDegreeMatrix(); - cacheLaplacianMatrix(); -} - -template -void Graph::addEdge(const Edge *edge) { - if (edge->isDirected().has_value() && edge->isDirected().value()) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edge_shared = make_shared>( - *dynamic_cast *>(edge)); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> elem = { - edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( - std::move(elem)); - } else { - auto edge_shared = make_shared>(*edge); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> 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>( - *dynamic_cast *>(edge)); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> elem = { - edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( - std::move(elem)); - std::pair>, shared>> elem1 = { - edge_shared->getNodePair().first, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( - std::move(elem1)); - } else { - auto edge_shared = make_shared>(*edge); - this->edgeSet.insert(edge_shared); - - std::pair>, shared>> elem = { - edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( - std::move(elem)); - std::pair>, shared>> elem1 = { - edge_shared->getNodePair().first, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( - std::move(elem1)); - } - } -} - -template -void Graph::addEdge(shared> 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>> elem = { - edge.get()->getNodePair().second, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( - std::move(elem)); - } else { - std::pair>, shared>> elem = { - edge.get()->getNodePair().second, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( - std::move(elem)); - std::pair>, shared>> elem1 = { - edge.get()->getNodePair().first, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().second].push_back( - std::move(elem1)); - } -} - -template -template -void Graph::addEdges() { - return; -} - -template -template -std::enable_if && (is_edge_ptr_v && ...), void> -Graph::addEdges(T1 edge, Tn... edges) { - addEdge(edge); - addEdges(edges...); -} - -template -void Graph::addNode(const Node *node) { - auto node_shared = make_shared>(*node); - this->isolatedNodesSet.insert(node_shared); -} - -template -void Graph::addNode(shared> node) { - this->isolatedNodesSet.insert(node); -} - -template -template -void Graph::addNodes() { - return; -} - -template -template -std::enable_if && (is_node_ptr_v && ...), void> -Graph::addNodes(T1 node, Tn... nodes) { - addNode(node); - addNodes(nodes...); -} - -template -void Graph::removeEdge(const CXXGraph::id_t edgeId) { - auto edgeOpt = Graph::getEdge(edgeId); - if (edgeOpt.has_value()) { - /* - edgeSet.erase(std::find_if(this->edgeSet.begin(), this->edgeSet.end(), - [edgeOpt](const Edge *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); - } - } -} - -template -void Graph::removeNode(const std::string &nodeUserId) { - auto nodeOpt = getNode(nodeUserId); - auto isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value()); - - if (nodeOpt.has_value() && isolatedNodeIt != isolatedNodesSet.end()) { - // The node is isolated - isolatedNodesSet.erase(isolatedNodeIt); - } else if (nodeOpt.has_value()) { - // The node is not isolated - // Remove the edges containing the node - auto edgeset = edgeSet; - for (auto edgeIt : edgeset) { - if (edgeIt->getNodePair().first->getUserId() == nodeUserId || - edgeIt->getNodePair().second->getUserId() == nodeUserId) { - this->removeEdge(edgeIt->getId()); - } - } - } -} - -template -bool Graph::findEdge(const Node *v1, const Node *v2, - CXXGraph::id_t &id) const { - auto v1_shared = make_shared>(*v1); - auto v2_shared = make_shared>(*v2); - - return findEdge(v1_shared, v2_shared, id); -} - -template -bool Graph::findEdge(shared> v1, shared> v2, - CXXGraph::id_t &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 - if (cachedAdjMatrix.get() != NULL && cachedAdjMatrix->size() != 0) { - /* 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; - } - if (!e->isDirected() && - ((e->getNodePair().second == v1) && (e->getNodePair().first == v2))) { - id = e->getId(); - return true; - } - }*/ - - id = 0; - return false; -} - -template -const T_NodeSet Graph::getNodeSet() const { - T_NodeSet nodeSet; - - for (const auto &edgeSetIt : edgeSet) { - nodeSet.insert(edgeSetIt->getNodePair().first); - nodeSet.insert(edgeSetIt->getNodePair().second); - } - // Merge with the isolated nodes - nodeSet.insert(this->isolatedNodesSet.begin(), this->isolatedNodesSet.end()); - - return nodeSet; -} - -template -const T_NodeSet Graph::getIsolatedNodeSet() const { - return isolatedNodesSet; -} - -template -void Graph::setNodeData(const std::string &nodeUserId, T data) { - auto nodeSet = this->nodeSet(); - auto nodeIt = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&nodeUserId](auto node) { return node->getUserId() == nodeUserId; }); - std::const_pointer_cast>(*nodeIt)->setData(std::move(data)); -} - -template -void Graph::setNodeData(std::map &dataMap) { - // Construct the set of all the nodes in the graph - for (auto &nodeSetIt : this->nodeSet()) { - nodeSetIt->setData(std::move(dataMap[nodeSetIt->getUserId()])); - } -} - -template -const std::optional>> Graph::getEdge( - const CXXGraph::id_t edgeId) const { - for (const auto &it : edgeSet) { - if (it->getId() == edgeId) { - return it; - } - } - - return std::nullopt; -} - -template -const std::optional>> Graph::getNode( - const std::string &nodeUserId) const { - for (const auto &it : getNodeSet()) { - if (it->getUserId() == nodeUserId) { - return it; - } - } - - return std::nullopt; -} - -template -std::unordered_set>, nodeHash> Graph::nodeSet() { - std::unordered_set>, nodeHash> nodeSet; - for (auto &edgeSetIt : edgeSet) { - nodeSet.insert( - std::const_pointer_cast>(edgeSetIt->getNodePair().first)); - nodeSet.insert( - std::const_pointer_cast>(edgeSetIt->getNodePair().second)); - } - for (auto &isNodeIt : isolatedNodesSet) { - nodeSet.insert(std::const_pointer_cast>(isNodeIt)); - } - - return nodeSet; -} - -template -std::optional> Graph::getExtenstionAndSeparator( - InputOutputFormat format) const { - if (format == InputOutputFormat::STANDARD_CSV) { - return std::pair(".csv", ','); - } else if (format == InputOutputFormat::STANDARD_TSV) { - return std::pair(".tsv", '\t'); - } else { - return std::nullopt; - } -} - -template -int Graph::writeToDot(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName) const { - const std::string linkSymbol = "--"; - const std::string directedLinkSymbol = "->"; - - const std::string completePathToFileGraph = - workingDir + '/' + OFileName + ".dot"; - std::ofstream ofileGraph; - ofileGraph.open(completePathToFileGraph); - if (!ofileGraph.is_open()) { - // ERROR File Not Open - return -1; - } - - // Write the header of the DOT file - std::string headerLine; - if (this->isDirectedGraph()) { - headerLine = "digraph " + graphName + " {\n"; - } else { - headerLine = "graph " + graphName + " {\n"; - } - ofileGraph << headerLine; - - for (auto const &edgePtr : edgeSet) { - std::string edgeLine = ""; - if (edgePtr->isDirected().has_value() && edgePtr->isDirected().value()) { - auto directedPtr = - std::static_pointer_cast>(edgePtr); - edgeLine += '\t' + directedPtr->getFrom().getUserId() + ' '; - edgeLine += directedLinkSymbol + ' '; - edgeLine += directedPtr->getTo().getUserId(); - } else { - edgeLine += '\t' + edgePtr->getNodePair().first->getUserId() + ' '; - edgeLine += linkSymbol + ' '; - edgeLine += edgePtr->getNodePair().second->getUserId(); - } - if (edgePtr->isWeighted().has_value() && edgePtr->isWeighted().value()) { - // Weights in dot files must be integers - edgeLine += " [weight=" + - std::to_string(static_cast( - std::dynamic_pointer_cast(edgePtr) - ->getWeight())) + - ']'; - } - edgeLine += ";\n"; - ofileGraph << edgeLine; - } - ofileGraph << '}'; - ofileGraph.close(); - - return 0; -} - -// This ctype facet classifies ',' and '\t' as whitespace -struct csv_whitespace : std::ctype { - static const mask *make_table() { - // make a copy of the "C" locale table - static std::vector v(classic_table(), classic_table() + table_size); - v[','] |= space; // comma will be classified as whitespace - v['\t'] |= space; - v[' '] &= ~space; // space will not be classified as whitespace - return &v[0]; - } - csv_whitespace(std::size_t refs = 0) : ctype(make_table(), false, refs) {} -}; - -template -void Graph::writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, - std::ostream &oEdgeWeight, const char &sep, - bool writeNodeFeat, - bool writeEdgeWeight) const { - for (const auto &edge : edgeSet) { - oGraph << edge->getId() << sep << edge->getNodePair().first->getUserId() - << sep << edge->getNodePair().second->getUserId() << sep - << ((edge->isDirected().has_value() && edge->isDirected().value()) - ? 1 - : 0) - << std::endl; - } - - if (writeNodeFeat) { - auto nodeSet = getNodeSet(); - for (const auto &node : nodeSet) { - oNodeFeat << node->getUserId() << sep << node->getData() << std::endl; - } - } - - if (writeEdgeWeight) { - for (const auto &edge : edgeSet) { - oEdgeWeight - << edge->getId() << sep - << (edge->isWeighted().has_value() && edge->isWeighted().value() - ? (std::dynamic_pointer_cast(edge)) - ->getWeight() - : 0.0) - << sep - << (edge->isWeighted().has_value() && edge->isWeighted().value() ? 1 - : 0) - << std::endl; - } - } -} - -template -void Graph::readGraphFromStream(std::istream &iGraph, - std::istream &iNodeFeat, - std::istream &iEdgeWeight, bool readNodeFeat, - bool readEdgeWeight) { - std::unordered_map> - edgeMap; - std::unordered_map edgeDirectedMap; - std::unordered_map nodeFeatMap; - std::unordered_map edgeWeightMap; - - CXXGraph::id_t edgeId; - std::string nodeId1; - std::string nodeId2; - bool directed; - while (iGraph >> edgeId >> nodeId1 >> nodeId2 >> - directed) { /* loop continually */ - edgeMap[edgeId] = std::pair(nodeId1, nodeId2); - edgeDirectedMap[edgeId] = directed; - } - - if (readNodeFeat) { - std::string nodeId; - T nodeFeat; - while (iNodeFeat >> nodeId >> nodeFeat) { - nodeFeatMap[nodeId] = nodeFeat; - } - } - - if (readEdgeWeight) { - CXXGraph::id_t edgeId; - double weight; - bool weighted; - while (iEdgeWeight >> edgeId >> weight >> weighted) { /* loop continually */ - if (weighted) { - edgeWeightMap[edgeId] = weight; - } - } - } - - recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); -} - -template -int Graph::readFromDot(const std::string &workingDir, - const std::string &fileName) { - // Define the edge maps - std::unordered_map> - edgeMap; - std::unordered_map nodeFeatMap; - std::unordered_map edgeDirectedMap; - std::unordered_map edgeWeightMap; - - // Define the node strings and the "garbage collector" temp string - std::string node1; - std::string node2; - std::string temp; - - // Get full path to the file and open it - const std::string completePathToFileGraph = - workingDir + '/' + fileName + ".dot"; - - // Check if the graph is directed - bool directed = false; - std::ifstream fileContentStream(completePathToFileGraph); - std::string fileContent(std::istreambuf_iterator{fileContentStream}, - {}); - if (fileContent.find("->") != std::string::npos) { - directed = true; - } - // Check if the graph is weighted - bool weighted = false; - if (fileContent.find("weight") != std::string::npos) { - weighted = true; - } - fileContentStream.close(); - - std::ifstream iFile(completePathToFileGraph); - // Write the header of the DOT file in the temp string - getline(iFile, temp); - - CXXGraph::id_t edgeId = 0; - std::string fileRow; - while (getline(iFile, fileRow)) { - // If you've reached the end of the file, stop - if (fileRow == "}") { - break; - } - - // Remove the whitespaces before the definition of the edge - while (*fileRow.begin() == ' ' || *fileRow.begin() == '\t') { - fileRow.erase(fileRow.begin()); - } - - std::stringstream row_stream(fileRow); - getline(row_stream, node1, ' '); - // Store the symbol representing the edge inside temp - getline(row_stream, temp, ' '); - if (weighted) { - getline(row_stream, node2, '['); - // Remove any whitespaces or tabs from the node string - node2.erase(std::remove(node2.begin(), node2.end(), ' '), node2.end()); - node2.erase(std::remove(node2.begin(), node2.end(), '\t'), node2.end()); - - getline(row_stream, temp, '='); - std::string weight; - getline(row_stream, weight, ']'); - // Erase any whitespaces - weight.erase(std::remove(weight.begin(), weight.end(), ' '), - weight.end()); - edgeWeightMap[edgeId] = std::stod(weight); - } else { - getline(row_stream, node2, ';'); - } - - // Save the edge and increment the edge counter - edgeMap[edgeId] = std::pair(node1, node2); - edgeDirectedMap[edgeId] = directed; - ++edgeId; - } - iFile.close(); - - recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); - return 0; -} - -template -void Graph::recreateGraph( - std::unordered_map> - &edgeMap, - std::unordered_map &edgeDirectedMap, - std::unordered_map &nodeFeatMap, - std::unordered_map &edgeWeightMap) { - std::unordered_map>> nodeMap; - for (const auto &edgeIt : edgeMap) { - shared> node1(nullptr); - shared> node2(nullptr); - if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { - feat = std::move(nodeFeatMap.at(edgeIt.second.first)); - } - node1 = make_shared>(edgeIt.second.first, feat); - nodeMap[edgeIt.second.first] = node1; - } else { - node1 = nodeMap.at(edgeIt.second.first); - } - if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { - feat = std::move(nodeFeatMap.at(edgeIt.second.second)); - } - node2 = make_shared>(edgeIt.second.second, feat); - nodeMap[edgeIt.second.second] = node2; - } else { - node2 = nodeMap.at(edgeIt.second.second); - } - - if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = make_shared>( - edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } else { - auto edge = make_shared>( - edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } - } else { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = make_shared>(edgeIt.first, node1, node2); - addEdge(edge); - } else { - auto edge = make_shared>(edgeIt.first, node1, node2); - addEdge(edge); - } - } - } -} - -#ifdef WITH_COMPRESSION -template -int Graph::compressFile(const std::string &inputFile, - const std::string &outputFile) const { - std::ifstream ifs; - ifs.open(inputFile); - if (!ifs.is_open()) { - // ERROR File Not Open - return -1; - } - std::string content((std::istreambuf_iterator(ifs)), - (std::istreambuf_iterator())); - - const char *content_ptr = content.c_str(); - gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); - if (outFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); - return -1; - } - - unsigned int zippedBytes; - zippedBytes = - gzwrite(outFileZ, content_ptr, (unsigned int)strlen(content_ptr)); - - ifs.close(); - gzclose(outFileZ); - return 0; -} - -template -int Graph::decompressFile(const std::string &inputFile, - const std::string &outputFile) const { - gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); - if (inFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); - return -1; - } - unsigned char unzipBuffer[8192]; - unsigned int unzippedBytes; - std::vector unzippedData; - std::ofstream ofs; - ofs.open(outputFile); - if (!ofs.is_open()) { - // ERROR File Not Open - return -1; - } - while (true) { - unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); - if (unzippedBytes > 0) { - unzippedData.insert(unzippedData.end(), unzipBuffer, - unzipBuffer + unzippedBytes); - } else { - break; - } - } - for (const auto &c : unzippedData) { - ofs << c; - } - ofs << std::endl; - ofs.close(); - gzclose(inFileZ); - return 0; -} -#endif - -template -CXXGraph::id_t Graph::setFind( - std::unordered_map *subsets, - const CXXGraph::id_t nodeId) const { - auto subsets_ptr = - make_shared>(*subsets); - // find root and make root as parent of i - // (path compression) - if ((*subsets)[nodeId].parent != nodeId) { - (*subsets)[nodeId].parent = - Graph::setFind(subsets_ptr, (*subsets)[nodeId].parent); - } - - return (*subsets)[nodeId].parent; -} - -template -CXXGraph::id_t Graph::setFind( - shared> subsets, - const CXXGraph::id_t nodeId) const { - // find root and make root as parent of i - // (path compression) - if ((*subsets)[nodeId].parent != nodeId) { - (*subsets)[nodeId].parent = - Graph::setFind(subsets, (*subsets)[nodeId].parent); - } - - return (*subsets)[nodeId].parent; -} - -template -void Graph::setUnion(std::unordered_map *subsets, - const CXXGraph::id_t elem1, - const CXXGraph::id_t elem2) const { - /* auto subsets_ptr = make_shared>(*subsets); */ - // if both sets have same parent - // then there's nothing to be done - /* if ((*subsets_ptr)[elem1].parent == (*subsets_ptr)[elem2].parent) return; - */ - /* auto elem1Parent = Graph::setFind(subsets_ptr, elem1); */ - /* auto elem2Parent = Graph::setFind(subsets_ptr, elem2); */ - /* if ((*subsets_ptr)[elem1Parent].rank < (*subsets_ptr)[elem2Parent].rank) */ - /* (*subsets_ptr)[elem1].parent = elem2Parent; */ - /* else if ((*subsets_ptr)[elem1Parent].rank > - * (*subsets_ptr)[elem2Parent].rank) */ - /* (*subsets_ptr)[elem2].parent = elem1Parent; */ - /* else { */ - /* (*subsets_ptr)[elem2].parent = elem1Parent; */ - /* (*subsets_ptr)[elem1Parent].rank++; */ - /* } */ - if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; - auto elem1Parent = Graph::setFind(subsets, elem1); - auto elem2Parent = Graph::setFind(subsets, elem2); - if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) - (*subsets)[elem1].parent = elem2Parent; - else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) - (*subsets)[elem2].parent = elem1Parent; - else { - (*subsets)[elem2].parent = elem1Parent; - (*subsets)[elem1Parent].rank++; - } -} - -template -void Graph::setUnion( - shared> subsets, - const CXXGraph::id_t elem1, const CXXGraph::id_t elem2) const { - // if both sets have same parent - // then there's nothing to be done - if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; - auto elem1Parent = Graph::setFind(subsets, elem1); - auto elem2Parent = Graph::setFind(subsets, elem2); - if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) - (*subsets)[elem1].parent = elem2Parent; - else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) - (*subsets)[elem2].parent = elem1Parent; - else { - (*subsets)[elem2].parent = elem1Parent; - (*subsets)[elem1Parent].rank++; - } -} - -template -std::shared_ptr>> Graph::eulerianPath() const { - const auto nodeSet = Graph::getNodeSet(); - - std::shared_ptr>> eulerPath = - std::make_shared>>(); - - bool undirected = this->isUndirectedGraph(); - - std::vector>> currentPath; - // The starting node is the only node which has more outgoing than ingoing - // links - auto firstNodeIt = std::max_element( - nodeSet.begin(), nodeSet.end(), [this](auto n1, auto n2) { - return cachedAdjMatrix->at(n1).size() < cachedAdjMatrix->at(n2).size(); - }); - auto currentNode = *(firstNodeIt); - currentPath.push_back(currentNode); - - while (currentPath.size() > 0) { - auto &edges = cachedAdjMatrix->at(currentNode); - // we keep removing the edges that - // have been traversed from the adjacency list - if (edges.size()) { - auto firstEdge = edges.back().second; - - shared> nextNodeId; - nextNodeId = firstEdge->getOtherNode(currentNode); - - currentPath.push_back(nextNodeId); - currentNode = nextNodeId; - edges.pop_back(); - } else { - eulerPath->push_back(*currentNode); - currentNode = currentPath.back(); - currentPath.pop_back(); - } - } - return eulerPath; -} - -template -shared> Graph::getAdjMatrix() const { - auto adj = std::make_shared>(); - auto addElementToAdjMatrix = [&adj](shared> nodeFrom, - shared> nodeTo, - shared> edge) { - std::pair>, shared>> elem = {nodeTo, - edge}; - (*adj)[nodeFrom].push_back(std::move(elem)); - }; - for (const auto &edgeSetIt : edgeSet) { - if (edgeSetIt->isDirected().has_value() && - edgeSetIt->isDirected().value()) { - shared> d_edge = - std::static_pointer_cast>(edgeSetIt); - addElementToAdjMatrix(d_edge->getNodePair().first, - d_edge->getNodePair().second, d_edge); - } else if (edgeSetIt->isDirected().has_value() && - !edgeSetIt->isDirected().value()) { - shared> ud_edge = - std::static_pointer_cast>(edgeSetIt); - ; - addElementToAdjMatrix(ud_edge->getNodePair().first, - ud_edge->getNodePair().second, ud_edge); - addElementToAdjMatrix(ud_edge->getNodePair().second, - ud_edge->getNodePair().first, ud_edge); - } else { // is a simple edge we cannot create adj matrix - return adj; - } - } - return adj; -} - -template -void Graph::cacheAdjMatrix() { - const auto adj = Graph::getAdjMatrix(); - this->cachedAdjMatrix = adj; -} - -template -shared> Graph::getDegreeMatrix() const { - auto degreeMatrix = std::make_shared>(); - - for (const auto &nodePair : *this->cachedAdjMatrix) { - const shared> &node = nodePair.first; - const std::vector>, shared>>> - &neighbors = nodePair.second; - - int degree = neighbors.size(); - - (*degreeMatrix)[node] = {degree}; - } - - return degreeMatrix; -} - -template -void Graph::cacheDegreeMatrix() { - const auto degreeMatrix = Graph::getDegreeMatrix(); - this->cachedDegreeMatrix = degreeMatrix; -} - -template -shared> Graph::getLaplacianMatrix() const { - const auto adjacencyMatrix = this->cachedAdjMatrix; - const auto degreeMatrix = this->cachedDegreeMatrix; - - auto laplacianMatrix = std::make_shared>(); - for (const auto &nodePair : *adjacencyMatrix) { - const shared> &node = nodePair.first; - (*laplacianMatrix)[node] = - std::vector>, shared>>>(); - } - - for (const auto &nodePair : *adjacencyMatrix) { - const shared> &node = nodePair.first; - const std::vector>, shared>>> - &neighbors = nodePair.second; - - int degree = neighbors.size(); - - (*laplacianMatrix)[node].emplace_back(node, - nullptr); // Insere o nó na diagonal - for (const auto &neighborPair : neighbors) { - const shared> &neighbor = neighborPair.first; - (*laplacianMatrix)[node].emplace_back( - neighbor, neighborPair.second); // Insere os pares de vizinhos - } - } - - return laplacianMatrix; -} - -template -void Graph::cacheLaplacianMatrix() { - const auto laplacianMatrix = Graph::getLaplacianMatrix(); - this->cachedLaplacianMatrix = laplacianMatrix; -} - -template -shared> Graph::getTransitionMatrix() const { - const auto adjacencyMatrix = this->cachedAdjMatrix; - - auto transitionMatrix = std::make_shared>(); - for (const auto &nodePair : *adjacencyMatrix) { - const shared> &node = nodePair.first; - (*transitionMatrix)[node] = - std::vector>, double>>(); - } - - for (const auto &nodePair : *adjacencyMatrix) { - const shared> &node = nodePair.first; - const std::vector>, shared>>> - &neighbors = nodePair.second; - - int degree = neighbors.size(); - - double transitionProbability = 1.0 / degree; - - for (const auto &neighborPair : neighbors) { - const shared> &neighbor = neighborPair.first; - (*transitionMatrix)[node].emplace_back(neighbor, transitionProbability); - } - } - - return transitionMatrix; -} - -template -void Graph::cacheTransitionMatrix() { - const auto transitionMatrix = Graph::getTransitionMatrix(); - this->cachedTransitionMatrix = transitionMatrix; -} - -template -const std::unordered_set>, nodeHash> -Graph::outNeighbors(const Node *node) const { - auto node_shared = make_shared>(*node); - - return outNeighbors(node_shared); -} - -template -const std::unordered_set>, nodeHash> -Graph::outNeighbors(shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, nodeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, nodeHash> outNeighbors; - for (auto pair : nodeEdgePairs) { - if (pair.second->isDirected().has_value() && - pair.second->isDirected().value()) { - outNeighbors.insert(pair.first); - } - } - - return outNeighbors; -} - -template -const std::unordered_set>, nodeHash> -Graph::inOutNeighbors(const Node *node) const { - auto node_shared = make_shared>(*node); - - return inOutNeighbors(node_shared); -} - -template -const std::unordered_set>, nodeHash> -Graph::inOutNeighbors(shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, nodeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, nodeHash> inOutNeighbors; - for (auto pair : nodeEdgePairs) { - inOutNeighbors.insert(pair.first); - } - - return inOutNeighbors; -} - -template -const std::unordered_set>, edgeHash> Graph::outEdges( - const Node *node) const { - auto node_shared = make_shared>(*node); - - return outEdges(node_shared); -} - -template -const std::unordered_set>, edgeHash> Graph::outEdges( - shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, edgeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, edgeHash> outEdges; - for (auto pair : nodeEdgePairs) { - if (pair.second->isDirected().has_value() && - pair.second->isDirected().value()) { - outEdges.insert(pair.second); - } - } - - return outEdges; -} - -template -const std::unordered_set>, edgeHash> -Graph::inOutEdges(const Node *node) const { - auto node_shared = make_shared>(*node); - - return outEdges(node_shared); -} - -template -const std::unordered_set>, edgeHash> -Graph::inOutEdges(shared> node) const { - if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { - return std::unordered_set>, edgeHash>(); - } - auto nodeEdgePairs = cachedAdjMatrix->at(node); - - std::unordered_set>, edgeHash> outEdges; - for (auto pair : nodeEdgePairs) { - outEdges.insert(pair.second); - } - - return outEdges; -} - -template -const DijkstraResult Graph::dijkstra(const Node &source, - const Node &target) const { - DijkstraResult result; - auto nodeSet = Graph::getNodeSet(); - - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - - auto target_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - if (target_node_it == nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - // n denotes the number of vertices in graph - auto n = cachedAdjMatrix->size(); - - // setting all the distances initially to INF_DOUBLE - std::unordered_map>, double, nodeHash> dist; - - for (const auto &node : nodeSet) { - dist[node] = INF_DOUBLE; - } - - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue>>, - std::vector>>>, - std::greater>>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - pq.push(std::make_pair(0.0, *source_node_it)); - - // marking the distance of source as 0 - dist[*source_node_it] = 0; - - std::unordered_map parent; - parent[source.getUserId()] = ""; - - while (!pq.empty()) { - // second element of pair denotes the node / vertex - shared> currentNode = pq.top().second; - // first element of pair denotes the distance - double currentDist = pq.top().first; - - pq.pop(); - - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - if (elem.second->isDirected().has_value() && - elem.second->isDirected().value()) { - shared> dw_edge = - std::static_pointer_cast>( - elem.second); - if (dw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { - dist[elem.first] = currentDist + dw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - parent[elem.first.get()->getUserId()] = - currentNode.get()->getUserId(); - } - } else if (elem.second->isDirected().has_value() && - !elem.second->isDirected().value()) { - shared> udw_edge = - std::static_pointer_cast>( - elem.second); - if (udw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { - dist[elem.first] = currentDist + udw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - parent[elem.first.get()->getUserId()] = - currentNode.get()->getUserId(); - } - } else { - // ERROR it shouldn't never returned ( does not exist a Node - // Weighted and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - } - } - if (dist[*target_node_it] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.result = dist[*target_node_it]; - std::string id = target.getUserId(); - while (parent[id] != "") { - result.path.push_back(id); - id = parent[id]; - } - result.path.push_back(source.getUserId()); - std::reverse(result.path.begin(), result.path.end()); - return result; - } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - return result; -} - -template -const BellmanFordResult Graph::bellmanford(const Node &source, - const Node &target) const { - BellmanFordResult result; - result.success = false; - result.errorMessage = ""; - result.result = INF_DOUBLE; - auto nodeSet = Graph::getNodeSet(); - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - auto target_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - if (target_node_it == nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - // setting all the distances initially to INF_DOUBLE - std::unordered_map>, double, nodeHash> dist, - currentDist; - // n denotes the number of vertices in graph - auto n = nodeSet.size(); - for (const auto &elem : nodeSet) { - dist[elem] = INF_DOUBLE; - currentDist[elem] = INF_DOUBLE; - } - - // marking the distance of source as 0 - dist[*source_node_it] = 0; - // set if node distances in two consecutive - // iterations remain the same. - auto earlyStopping = false; - // outer loop for vertex relaxation - for (int i = 0; i < n - 1; ++i) { - auto edgeSet = Graph::getEdgeSet(); - // inner loop for distance updates of - // each relaxation - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edge_weight = - (std::dynamic_pointer_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) - dist[elem.second] = dist[elem.first] + edge_weight; - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - auto flag = true; - for (const auto &[key, value] : dist) { - if (currentDist[key] != value) { - flag = false; - break; - } - } - for (const auto &[key, value] : dist) { - currentDist[key] = value; // update the current distance - } - if (flag) { - earlyStopping = true; - break; - } - } - - // check if there exists a negative cycle - if (!earlyStopping) { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edge_weight = - (std::dynamic_pointer_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) { - result.success = true; - result.negativeCycle = true; - result.errorMessage = ""; - return result; - } - } - } - - if (dist[*target_node_it] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.negativeCycle = false; - result.result = dist[*target_node_it]; - return result; - } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - result.result = -1; - return result; -} - -/* - * See Harry Hsu. "An algorithm for finding a minimal equivalent graph of a - * digraph.", Journal of the ACM, 22(1):11-16, January 1975 - * - * foreach x in graph.vertices - * foreach y in graph.vertices - * foreach z in graph.vertices - * delete edge xz if edges xy and yz exist - */ -template -const Graph Graph::transitiveReduction() const { - Graph result(this->edgeSet); - - CXXGraph::id_t edgeId = 0; - std::unordered_set>, nodeHash> nodes = - this->getNodeSet(); - for (auto x : nodes) { - for (auto y : nodes) { - if (this->findEdge(x, y, edgeId)) { - for (auto z : nodes) { - if (this->findEdge(y, z, edgeId)) { - if (this->findEdge(x, z, edgeId)) { - result.removeEdge(edgeId); - } - } - } - } - } - } - - return result; -} - -template -const FWResult Graph::floydWarshall() const { - FWResult result; - result.success = false; - result.errorMessage = ""; - std::unordered_map, double, - CXXGraph::pair_hash> - pairwise_dist; - const auto &nodeSet = Graph::getNodeSet(); - // create a pairwise distance matrix with distance node distances - // set to inf. Distance of node to itself is set as 0. - for (const auto &elem1 : nodeSet) { - for (const auto &elem2 : nodeSet) { - auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); - if (elem1 != elem2) - pairwise_dist[key] = INF_DOUBLE; - else - pairwise_dist[key] = 0.0; - } - } - - const auto &edgeSet = Graph::getEdgeSet(); - // update the weights of nodesfloydWarshall - // connected by edges - for (const auto &edge : edgeSet) { - const auto &elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edgeWeight = - (std::dynamic_pointer_cast(edge))->getWeight(); - auto key = - std::make_pair(elem.first->getUserId(), elem.second->getUserId()); - pairwise_dist[key] = edgeWeight; - if (edge->isDirected() == false) { - auto reverseKey = - std::make_pair(elem.second->getUserId(), elem.first->getUserId()); - pairwise_dist[reverseKey] = edgeWeight; - } - } else { - // if an edge exists but has no weight associated - // with it, we return an error message - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - - for (const auto &k : nodeSet) { - // set all vertices as source one by one - for (const auto &src : nodeSet) { - // iterate through all vertices as destination for the - // current source - auto src_k = std::make_pair(src->getUserId(), k->getUserId()); - for (const auto &dst : nodeSet) { - // If vertex k provides a shorter path than - // src to dst, update the value of - // pairwise_dist[src_to_dst] - auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); - auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); - if (pairwise_dist[src_dst] > - (pairwise_dist[src_k] + pairwise_dist[k_dst]) && - (pairwise_dist[k_dst] != INF_DOUBLE && - pairwise_dist[src_k] != INF_DOUBLE)) - pairwise_dist[src_dst] = pairwise_dist[src_k] + pairwise_dist[k_dst]; - } - } - } - - result.success = true; - // presense of negative number in the diagonal indicates - // that that the graph contains a negative cycle - for (const auto &node : nodeSet) { - auto diag = std::make_pair(node->getUserId(), node->getUserId()); - if (pairwise_dist[diag] < 0.) { - result.negativeCycle = true; - return result; - } - } - result.result = std::move(pairwise_dist); - return result; -} - -template -const MstResult Graph::prim() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - if (!isConnectedGraph()) { - result.errorMessage = ERR_NOT_STRONG_CONNECTED; - return result; - } - auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - - // setting all the distances initially to INF_DOUBLE - std::unordered_map>, double, nodeHash> dist; - for (const auto &elem : (*cachedAdjMatrix)) { - dist[elem.first] = INF_DOUBLE; - } - - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue>>, - std::vector>>>, - std::greater>>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - auto source = *(nodeSet.begin()); - pq.push(std::make_pair(0.0, source)); - result.mstCost = 0; - std::vector doneNode; - // mark source node as done - // otherwise we get (0, 0) also in mst - doneNode.push_back(source->getId()); - // stores the parent and corresponding child node - // of the edges that are part of MST - std::unordered_map parentNode; - while (!pq.empty()) { - // second element of pair denotes the node / vertex - shared> currentNode = pq.top().second; - auto nodeId = currentNode->getId(); - if (std::find(doneNode.begin(), doneNode.end(), nodeId) == doneNode.end()) { - auto pair = std::make_pair(parentNode[nodeId], currentNode->getUserId()); - result.mst.push_back(pair); - result.mstCost += pq.top().first; - doneNode.push_back(nodeId); - } - - pq.pop(); - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - shared> udw_edge = - std::static_pointer_cast>( - elem.second); - if ((udw_edge->getWeight() < dist[elem.first]) && - (std::find(doneNode.begin(), doneNode.end(), - elem.first->getId()) == doneNode.end())) { - dist[elem.first] = udw_edge->getWeight(); - parentNode[elem.first->getId()] = currentNode->getUserId(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - } - } - result.success = true; - return result; -} - -template -const MstResult Graph::boruvka() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - const auto nodeSet = Graph::getNodeSet(); - const auto n = nodeSet.size(); - - // Use std map for storing n subsets. - auto subsets = make_shared>(); - - // Initially there are n different trees. - // Finally there will be one tree that will be MST - auto numTrees = n; - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - const auto edgeSet = Graph::getEdgeSet(); - std::unordered_map edgeWeight; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) - edgeWeight[edge->getId()] = - (std::dynamic_pointer_cast(edge))->getWeight(); - else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - (*subsets)[node->getId()] = set; - } - - result.mstCost = 0; // we will store the cost here - // exit when only 1 tree i.e. mst - while (numTrees > 1) { - // Everytime initialize cheapest map - // It stores index of the cheapest edge of subset. - std::unordered_map cheapest; - for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; - - // Traverse through all edges and update - // cheapest of every component - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edgeId = edge->getId(); - // Find sets of two corners of current edge - auto set1 = Graph::setFind(subsets, elem.first->getId()); - auto set2 = Graph::setFind(subsets, elem.second->getId()); - - // If two corners of current edge belong to - // same set, ignore current edge - if (set1 == set2) continue; - - // Else check if current edge is closer to previous - // cheapest edges of set1 and set2 - if (cheapest[set1] == INT_MAX || - edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) - cheapest[set1] = edgeId; - - if (cheapest[set2] == INT_MAX || - edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) - cheapest[set2] = edgeId; - } - - // iterate over all the vertices and add picked - // cheapest edges to MST - for (const auto &[nodeId, edgeId] : cheapest) { - // Check if cheapest for current set exists - if (edgeId != INT_MAX) { - auto cheapestNode = Graph::getEdge(edgeId).value()->getNodePair(); - auto set1 = Graph::setFind(subsets, cheapestNode.first->getId()); - auto set2 = Graph::setFind(subsets, cheapestNode.second->getId()); - if (set1 == set2) continue; - result.mstCost += edgeWeight[edgeId]; - auto newEdgeMST = std::make_pair(cheapestNode.first->getUserId(), - cheapestNode.second->getUserId()); - result.mst.push_back(newEdgeMST); - // take union of set1 and set2 and decrease number of trees - Graph::setUnion(subsets, set1, set2); - numTrees--; - } - } - } - result.success = true; - return result; -} - -template -const MstResult Graph::kruskal() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - const auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - auto edgeSet = Graph::getEdgeSet(); - std::priority_queue>>, - std::vector>>>, - std::greater>>>> - sortedEdges; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto weight = - (std::dynamic_pointer_cast(edge))->getWeight(); - sortedEdges.push(std::make_pair(weight, edge)); - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - - auto subset = make_shared>(); - - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - (*subset)[node->getId()] = set; - } - result.mstCost = 0; - while ((!sortedEdges.empty()) && (result.mst.size() < n)) { - auto [edgeWeight, cheapestEdge] = sortedEdges.top(); - sortedEdges.pop(); - auto &[first, second] = cheapestEdge->getNodePair(); - auto set1 = Graph::setFind(subset, first->getId()); - auto set2 = Graph::setFind(subset, second->getId()); - if (set1 != set2) { - result.mst.push_back( - std::make_pair(first->getUserId(), second->getUserId())); - result.mstCost += edgeWeight; - } - Graph::setUnion(subset, set1, set2); - } - result.success = true; - return result; -} - -template -BestFirstSearchResult Graph::best_first_search( - const Node &source, const Node &target) const { - BestFirstSearchResult result; - auto &nodeSet = Graph::getNodeSet(); - using pq_type = std::pair>>; - - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - - auto target_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - if (target_node_it == nodeSet.end()) { - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - - std::priority_queue, std::greater> pq; - - std::vector> visited; - visited.push_back(source); - pq.push(std::make_pair(0.0, *source_node_it)); - - while (!pq.empty()) { - shared> currentNode = pq.top().second; - pq.pop(); - result.nodesInBestSearchOrder.push_back(*currentNode); - - if (*currentNode == target) { - break; - } - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(currentNode)) { - if (elem.second->isWeighted().has_value()) { - if (elem.second->isDirected().has_value()) { - shared> dw_edge = - std::static_pointer_cast>( - elem.second); - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); - } - } else { - shared> dw_edge = - std::static_pointer_cast>( - elem.second); - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); - } - } - } else { - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - result.nodesInBestSearchOrder.clear(); - return result; - } - } - } - } - - result.success = true; - return result; -} - -template -const std::vector> Graph::breadth_first_search( - const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto &nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return visited; - } - // queue that stores vertices that need to be further explored - std::queue>> tracker; - - // mark the starting node as visited - visited.push_back(start); - tracker.push(*start_node_it); - while (!tracker.empty()) { - shared> node = tracker.front(); - tracker.pop(); - if (cachedAdjMatrix->find(node) != cachedAdjMatrix->end()) { - for (const auto &elem : cachedAdjMatrix->at(node)) { - // if the node is not visited then mark it as visited - // and push it to the queue - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - tracker.push(elem.first); - } - } - } - } - - return visited; -} - -template -const std::vector> Graph::concurrency_breadth_first_search( - const Node &start, size_t num_threads) const { - std::vector> bfs_result; - // check is exist node in the graph - auto &nodeSet = Graph::getNodeSet(); - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return bfs_result; - } - - std::unordered_map>, size_t, nodeHash> node_to_index; - for (const auto &node : nodeSet) { - node_to_index[node] = node_to_index.size(); - } - std::vector visited(nodeSet.size(), 0); - - // parameter limitations - if (num_threads <= 0) { - std::cout << "Error: number of threads should be greater than 0" - << std::endl; - num_threads = 2; - } - - // vector that stores vertices to be visit - std::vector>> level_tracker, next_level_tracker; - level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - - // mark the starting node as visited - visited[node_to_index[*start_node_it]] = 1; - level_tracker.push_back(*start_node_it); - - // a worker is assigned a small part of tasks for each time - // assignments of tasks in current level and updates of tasks in next - // level are inclusive - std::mutex tracker_mutex; - std::mutex next_tracker_mutex; - std::atomic assigned_tasks = 0; - int num_tasks = 1; - // unit of task assignment, which mean assign block_size tasks to a - // worker each time - int block_size = 1; - int level = 1; - - auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, - &num_tasks, &block_size]() -> std::pair { - /* - std::lock_guard tracker_guard(tracker_mutex); - int task_block_size = std::min(num_tasks - assigned_tasks, - block_size); std::pair task_block{assigned_tasks, - assigned_tasks + task_block_size}; assigned_tasks += task_block_size; - return task_block; - */ - int start = assigned_tasks.fetch_add(block_size); - int end = std::min(num_tasks, start + block_size); - return {start, end}; - }; - - auto submit_result = - [&next_level_tracker, &next_tracker_mutex]( - std::vector>> &submission) -> void { - std::lock_guard tracker_guard(next_tracker_mutex); - next_level_tracker.insert(std::end(next_level_tracker), - std::begin(submission), std::end(submission)); - }; - - // worker thread sleep until it begin to search nodes of next level - std::mutex next_level_mutex; - std::condition_variable next_level_cond; - std::atomic waiting_workers = 0; - - auto bfs_worker = [&]() -> void { - // algorithm is not done - while (!level_tracker.empty()) { - // search for nodes in a level is not done - std::vector>> local_tracker; - while (true) { - auto [start_index, end_index] = extract_tasks(); - if (start_index >= end_index) { - break; - } - - for (int i = start_index; i < end_index; ++i) { - if (cachedAdjMatrix->count(level_tracker[i])) { - for (const auto &elem : cachedAdjMatrix->at(level_tracker[i])) { - int index = (int)node_to_index[elem.first]; - if (visited[index] == 0) { - visited[index] = 1; - local_tracker.push_back(elem.first); - } - } - } - } - } - - // submit local result to global result - if (!local_tracker.empty()) { - submit_result(local_tracker); - } - - // last worker need to do preparation for the next iteration - int cur_level = level; - if (num_threads == 1 + waiting_workers.fetch_add(1)) { - swap(level_tracker, next_level_tracker); - next_level_tracker.clear(); - - // adjust block_size according to number of nodes in next level - block_size = 4; - if (level_tracker.size() <= num_threads * 4) { - block_size = std::max( - 1, static_cast(std::ceil( - static_cast(level_tracker.size()) / num_threads))); - } else if (level_tracker.size() >= num_threads * 64) { - block_size = 16; - } - - num_tasks = (int)level_tracker.size(); - waiting_workers = 0; - assigned_tasks = 0; - level = level + 1; - next_level_cond.notify_all(); - } else { - // not to wait if last worker reachs last statement before notify - // all or even further - std::unique_lock next_level_lock(next_level_mutex); - next_level_cond.wait(next_level_lock, [&level, cur_level]() { - return level != cur_level; - }); - } - } - }; - - std::vector workers; - for (int i = 0; i < num_threads - 1; ++i) { - workers.emplace_back(std::thread(bfs_worker)); - } - bfs_worker(); - - for (auto &worker : workers) { - if (worker.joinable()) { - worker.join(); - } - } - - for (const auto &visited_node : nodeSet) { - if (visited[node_to_index[visited_node]] != 0) { - bfs_result.push_back(*visited_node); - } - } - - return bfs_result; -} -template -const std::vector> Graph::depth_first_search( - const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return visited; - } - std::function>, - shared>, std::vector> &)> - explore; - explore = [&explore](const std::shared_ptr> adj, - shared> node, - std::vector> &visited) -> void { - visited.push_back(*node); - if (adj->find(node) != adj->end()) { - for (const auto &x : adj->at(node)) { - if (std::find(visited.begin(), visited.end(), *(x.first)) == - visited.end()) { - explore(adj, x.first, visited); - } - } - } - }; - explore(cachedAdjMatrix, *start_node_it, visited); - - return visited; -} - -template -bool Graph::isCyclicDirectedGraphDFS() const { - if (!isDirectedGraph()) { - return false; - } - enum nodeStates : uint8_t { not_visited, in_stack, visited }; - auto nodeSet = Graph::getNodeSet(); - - /* State of the node. - * - * It is a vector of "nodeStates" which represents the state node is in. - * It can take only 3 values: "not_visited", "in_stack", and "visited". - * - * Initially, all nodes are in "not_visited" state. - */ - std::unordered_map state; - for (const auto &node : nodeSet) { - state[node->getId()] = not_visited; - } - int stateCounter = 0; - - // Start visiting each node. - for (const auto &node : nodeSet) { - // If a node is not visited, only then check for presence of cycle. - // There is no need to check for presence of cycle for a visited - // node as it has already been checked for presence of cycle. - if (state[node->getId()] == not_visited) { - // Check for cycle. - std::function>, - std::unordered_map &, - shared>)> - isCyclicDFSHelper; - isCyclicDFSHelper = - [this, &isCyclicDFSHelper]( - const std::shared_ptr> adjMatrix, - std::unordered_map &states, - shared> node) { - // Add node "in_stack" state. - states[node->getId()] = in_stack; - - // If the node has children, then recursively visit all - // children of the node. - auto const it = adjMatrix->find(node); - if (it != adjMatrix->end()) { - for (const auto &child : it->second) { - // If state of child node is "not_visited", evaluate that - // child for presence of cycle. - auto state_of_child = states.at((std::get<0>(child))->getId()); - if (state_of_child == not_visited) { - if (isCyclicDFSHelper(adjMatrix, states, - std::get<0>(child))) { - return true; - } - } else if (state_of_child == in_stack) { - // If child node was "in_stack", then that means that - // there is a cycle in the graph. Return true for - // presence of the cycle. - return true; - } - } - } - - // Current node has been evaluated for the presence of cycle - // and had no cycle. Mark current node as "visited". - states[node->getId()] = visited; - // Return that current node didn't result in any cycles. - return false; - }; - if (isCyclicDFSHelper(cachedAdjMatrix, state, node)) { - return true; - } - } - } - - // All nodes have been safely traversed, that means there is no cycle in - // the graph. Return false. - return false; -} - -template -bool Graph::containsCycle(const T_EdgeSet *edgeSet) const { - auto edgeSet_ptr = make_shared>(*edgeSet); - auto subset = make_shared>(); - // initialize the subset parent and rank values - for (const auto &edge : *edgeSet_ptr) { - auto &[first, second] = edge->getNodePair(); - std::vector nodeId(2); - nodeId.push_back(first->getId()); - nodeId.push_back(second->getId()); - for (const auto &id : nodeId) { - auto nodeExists = [id](const auto &it) { - return (id == (it.second).parent); - }; - - if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == - (*subset).end()) { - Subset set; - set.parent = id; - set.rank = 0; - (*subset)[id] = set; - } - } - } - return Graph::containsCycle(edgeSet_ptr, subset); -} - -template -bool Graph::containsCycle(shared> edgeSet) const { - auto subset = make_shared>(); - // initialize the subset parent and rank values - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - std::vector nodeId(2); - nodeId.push_back(first->getId()); - nodeId.push_back(second->getId()); - for (const auto &id : nodeId) { - auto nodeExists = [id](const auto &it) { - return (id == (it.second).parent); - }; - - if (std::find_if((*subset).begin(), (*subset).end(), nodeExists) == - (*subset).end()) { - Subset set; - set.parent = id; - set.rank = 0; - (*subset)[id] = set; - } - } - } - return Graph::containsCycle(edgeSet, subset); -} - -template -bool Graph::containsCycle( - shared> edgeSet, - shared> subset) const { - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - auto set1 = Graph::setFind(subset, first->getId()); - auto set2 = Graph::setFind(subset, second->getId()); - if (set1 == set2) return true; - Graph::setUnion(subset, set1, set2); - } - return false; -} - -template -bool Graph::isCyclicDirectedGraphBFS() const { - if (!isDirectedGraph()) { - return false; - } - auto nodeSet = Graph::getNodeSet(); - - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - // Calculate the indegree i.e. the number of incident edges to the node. - for (auto const &list : (*cachedAdjMatrix)) { - auto children = list.second; - for (auto const &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } - - std::queue>> can_be_solved; - for (const auto &node : nodeSet) { - // If a node doesn't have any input edges, then that node will - // definately not result in a cycle and can be visited safely. - if (!indegree[node->getId()]) { - can_be_solved.emplace(node); - } - } - - // Vertices that need to be traversed. - auto remain = nodeSet.size(); - // While there are safe nodes that we can visit. - while (!can_be_solved.empty()) { - auto solved = can_be_solved.front(); - // Visit the node. - can_be_solved.pop(); - // Decrease number of nodes that need to be traversed. - remain--; - - // Visit all the children of the visited node. - auto it = cachedAdjMatrix->find(solved); - if (it != cachedAdjMatrix->end()) { - for (const auto &child : it->second) { - // Check if we can visited the node safely. - if (--indegree[std::get<0>(child)->getId()] == 0) { - // if node can be visited safely, then add that node to - // the visit queue. - can_be_solved.emplace(std::get<0>(child)); - } - } - } - } - - // If there are still nodes that we can't visit, then it means that - // there is a cycle and return true, else return false. - return !(remain == 0); -} - -template -bool Graph::isDirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if (!(edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Undirected Edge - return false; - } - } - // No Undirected Edge - return true; -} - -template -bool Graph::isUndirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if ((edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Directed Edge - return false; - } - } - // No Directed Edge - return true; -} - -template -void Graph::reverseDirectedGraph() { - if (!isDirectedGraph()) { - throw std::runtime_error(ERR_UNDIR_GRAPH); - } - auto oldEdgeSet = Graph::getEdgeSet(); - for (const auto &edge : oldEdgeSet) { - auto &[first, second] = edge->getNodePair(); - auto id = edge->getId(); - this->removeEdge(id); - auto newEdge = std::make_shared>(id, second, first); - this->addEdge(newEdge); - } -} - -template -bool Graph::isConnectedGraph() const { - if (!isUndirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function>)> dfs_helper = - [this, &visited, &dfs_helper](shared> source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { - shared> neighbor = - (*cachedAdjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - }; - // call dfs_helper for the first node - dfs_helper(*(nodeSet.begin())); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; - } - } - return true; - } -} - -template -bool Graph::isStronglyConnectedGraph() const { - if (!isDirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - for (const auto &start_node : nodeSet) { - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function>)> dfs_helper = - [this, &visited, &dfs_helper](shared> source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { - shared> neighbor = - (*cachedAdjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - }; - // call dfs_helper for the first node - dfs_helper(start_node); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; - } - } - } - return true; - } -} - -template -const TarjanResult Graph::tarjan(const unsigned int typeMask) const { - TarjanResult result; - result.success = false; - bool isDirected = this->isDirectedGraph(); - if (isDirected) { - // check whether targetMask is a subset of the mask for directed graph - unsigned int directedMask = TARJAN_FIND_SCC; - if ((typeMask | directedMask) != directedMask) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - } else { - // check whether targetMask is a subset of the mask for undirected graph - unsigned int undirectedMask = (TARJAN_FIND_CUTV | TARJAN_FIND_BRIDGE | - TARJAN_FIND_VBCC | TARJAN_FIND_EBCC); - if ((typeMask | undirectedMask) != undirectedMask) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } - } - - const auto &nodeSet = getNodeSet(); - std::unordered_map - discoveryTime; // the timestamp when a node is visited - std::unordered_map - lowestDisc; // the lowest discovery time of all - // reachable nodes from current node - int timestamp = 0; - CXXGraph::id_t rootId = 0; - std::stack> sccNodeStack; - std::stack> ebccNodeStack; - std::stack> vbccNodeStack; - std::unordered_set inStack; - std::function>, const shared>)> - dfs_helper = [this, typeMask, isDirected, &dfs_helper, &discoveryTime, - &lowestDisc, ×tamp, &rootId, &sccNodeStack, - &ebccNodeStack, &vbccNodeStack, &inStack, - &result](const shared> curNode, - const shared> prevEdge) { - // record the visited time of current node - discoveryTime[curNode->getId()] = timestamp; - lowestDisc[curNode->getId()] = timestamp; - timestamp++; - if (typeMask & TARJAN_FIND_SCC) { - sccNodeStack.emplace(*curNode); - inStack.emplace(curNode->getId()); - } - if (typeMask & TARJAN_FIND_EBCC) { - ebccNodeStack.emplace(*curNode); - } - if (typeMask & TARJAN_FIND_VBCC) { - vbccNodeStack.emplace(*curNode); - } - // travel the neighbors - int numSon = 0; - bool nodeIsAdded = - false; // whether a node has been marked as a cut vertice - if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { - for (const auto &[neighborNode, edge] : - cachedAdjMatrix->at(curNode)) { - if (!discoveryTime.count(neighborNode->getId())) { - dfs_helper(neighborNode, edge); - lowestDisc[curNode->getId()] = - std::min(lowestDisc[curNode->getId()], - lowestDisc[neighborNode->getId()]); - - if (typeMask & TARJAN_FIND_BRIDGE) { - // lowestDisc of neighbor node is larger than that of current - // node means we can travel back to a visited node only through - // this edge - if (discoveryTime[curNode->getId()] < - lowestDisc[neighborNode->getId()]) { - result.bridges.emplace_back(*edge); - } - } - - if ((typeMask & TARJAN_FIND_CUTV) && (nodeIsAdded == false)) { - if (curNode->getId() == rootId) { - numSon++; - // a root node is a cut vertices only when it connects at - // least two connected components - if (numSon == 2) { - nodeIsAdded = true; - result.cutVertices.emplace_back(*curNode); - } - } else { - if (discoveryTime[curNode->getId()] <= - lowestDisc[neighborNode->getId()]) { - nodeIsAdded = true; - result.cutVertices.emplace_back(*curNode); - } - } - } - - if (typeMask & TARJAN_FIND_VBCC) { - if (discoveryTime[curNode->getId()] <= - lowestDisc[neighborNode->getId()]) { - // if current node is a cut vertice or the root node, the vbcc - // a vertice-biconnect-component which contains the neighbor - // node - std::vector> vbcc; - while (true) { - // pop a top node out of stack until - // the neighbor node has been poped out - Node nodeAtTop = vbccNodeStack.top(); - vbccNodeStack.pop(); - vbcc.emplace_back(nodeAtTop); - if (nodeAtTop == *neighborNode) { - break; - } - } - vbcc.emplace_back(*curNode); - result.verticeBiconnectedComps.emplace_back(std::move(vbcc)); - } - } - } else if ((edge != prevEdge) && - ((isDirected == false) || - (inStack.count(neighborNode->getId())))) { - // it's not allowed to go through the previous edge back - // for a directed graph, it's also not allowed to visit - // a node that is not in stack - lowestDisc[curNode->getId()] = - std::min(lowestDisc[curNode->getId()], - lowestDisc[neighborNode->getId()]); - } - } - } - // find sccs for a undirected graph is very similar with - // find ebccs for a directed graph - if ((typeMask & TARJAN_FIND_SCC) || (typeMask & TARJAN_FIND_EBCC)) { - std::stack> &nodeStack = - (typeMask & TARJAN_FIND_SCC) ? sccNodeStack : ebccNodeStack; - if (discoveryTime[curNode->getId()] == lowestDisc[curNode->getId()]) { - std::vector> connectedComp; - while (true) { - // pop a top node out of stack until - // the current node has been poped out - Node nodeAtTop = nodeStack.top(); - nodeStack.pop(); - if (typeMask & TARJAN_FIND_SCC) { - inStack.erase(nodeAtTop.getId()); - } - connectedComp.emplace_back(nodeAtTop); - if (nodeAtTop == *curNode) { - break; - } - } - // store this component in result - (typeMask & TARJAN_FIND_SCC) - ? result.stronglyConnectedComps.emplace_back( - std::move(connectedComp)) - : result.edgeBiconnectedComps.emplace_back( - std::move(connectedComp)); - } - } - }; - - for (const auto &node : nodeSet) { - if (!discoveryTime.count(node->getId())) { - rootId = node->getId(); - dfs_helper(node, nullptr); - } - } - - result.success = true; - return result; -} - -template -TopoSortResult Graph::topologicalSort() const { - TopoSortResult result; - result.success = false; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else if (isCyclicDirectedGraphBFS()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - return result; - } else { - const auto &nodeSet = getNodeSet(); - std::unordered_map>, bool, nodeHash> visited; - - std::function>)> postorder_helper = - [this, &postorder_helper, &visited, - &result](shared> curNode) { - visited[curNode] = true; - - if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { - for (const auto &edge : cachedAdjMatrix->at(curNode)) { - const auto &nextNode = edge.first; - if (false == visited[nextNode]) { - postorder_helper(nextNode); - } - } - } - - result.nodesInTopoOrder.push_back(*curNode); - }; - - auto numNodes = cachedAdjMatrix->size(); - result.nodesInTopoOrder.reserve(numNodes); - - for (const auto &node : nodeSet) { - if (false == visited[node]) { - postorder_helper(node); - } - } - - result.success = true; - std::reverse(result.nodesInTopoOrder.begin(), - result.nodesInTopoOrder.end()); - return result; - } -} - -template -TopoSortResult Graph::kahn() const { - TopoSortResult result; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - const auto nodeSet = Graph::getNodeSet(); - result.nodesInTopoOrder.reserve(cachedAdjMatrix->size()); - - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - for (const auto &list : *cachedAdjMatrix) { - auto children = list.second; - for (const auto &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } - - std::queue>> topologicalOrder; - - for (const auto &node : nodeSet) { - if (!indegree[node->getId()]) { - topologicalOrder.emplace(node); - } - } - - size_t visited = 0; - while (!topologicalOrder.empty()) { - shared> currentNode = topologicalOrder.front(); - topologicalOrder.pop(); - result.nodesInTopoOrder.push_back(*currentNode); - - if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { - for (const auto &child : cachedAdjMatrix->at(currentNode)) { - if (--indegree[std::get<0>(child)->getId()] == 0) { - topologicalOrder.emplace(std::get<0>(child)); - } - } - } - visited++; - } - - if (visited != nodeSet.size()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - result.nodesInTopoOrder.clear(); - return result; - } - - result.success = true; - return result; - } -} - -template -SCCResult Graph::kosaraju() const { - SCCResult result; - result.success = false; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - auto nodeSet = getNodeSet(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - - std::stack>> st; - std::function>)> dfs_helper = - [this, &visited, &dfs_helper, &st](shared> source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { - shared> neighbor = - (*cachedAdjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - - st.push(source); - }; - - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - dfs_helper(node); - } - } - - // construct the transpose of the given graph - AdjacencyMatrix rev; - auto addElementToAdjMatrix = [&rev](shared> nodeFrom, - shared> nodeTo, - shared> edge) { - std::pair>, shared>> elem = {nodeTo, - edge}; - rev[nodeFrom].push_back(std::move(elem)); - }; - for (const auto &edgeSetIt : edgeSet) { - shared> d_edge = - std::static_pointer_cast>(edgeSetIt); - // Add the reverse edge to the reverse adjacency matrix - addElementToAdjMatrix(d_edge->getNodePair().second, - d_edge->getNodePair().first, d_edge); - } - - visited.clear(); - - std::function>, SCCResult, int)> dfs_helper1 = - [this, &rev, &visited, &dfs_helper1]( - shared> source, SCCResult result, int sccLabel) { - // mark the vertex visited - visited[source->getId()] = true; - // Add the current vertex to the strongly connected - // component - // comp.push_back(*source); - result.sccMap[source->getId()] = sccLabel; - - // travel the neighbors - for (int i = 0; i < rev[source].size(); i++) { - shared> neighbor = rev[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper1(neighbor, result, sccLabel); - } - } - }; - - int sccLabel = 0; - while (st.size() != 0) { - auto rem = st.top(); - st.pop(); - if (visited[rem->getId()] == false) { - // std::vector> comp; - dfs_helper1(rem, result, sccLabel); - sccLabel++; - // result.stronglyConnectedComps.push_back(comp); - } - } - result.noOfComponents = sccLabel; - result.success = true; - return result; - } -} - -template -const DialResult Graph::dial(const Node &source, int maxWeight) const { - DialResult result; - result.success = false; - - auto nodeSet = getNodeSet(); - - auto source_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - if (source_node_it == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - /* With each distance, iterator to that vertex in - its bucket is stored so that vertex can be deleted - in O(1) at time of updation. So - dist[i].first = distance of ith vertex from src vertex - dits[i].second = vertex i in bucket number */ - auto V = nodeSet.size(); - std::unordered_map>, - std::pair>>, nodeHash> - dist; - - // Initialize all distances as infinite (INF) - for (const auto &node : nodeSet) { - dist[node].first = std::numeric_limits::max(); - } - - // Create buckets B[]. - // B[i] keep vertex of distance label i - std::vector>>> B((maxWeight * V + 1)); - - B[0].push_back(*source_node_it); - dist[*source_node_it].first = 0; - - int idx = 0; - while (true) { - // Go sequentially through buckets till one non-empty - // bucket is found - while (B[idx].size() == 0u && idx < maxWeight * V) { - idx++; - } - - // If all buckets are empty, we are done. - if (idx == maxWeight * V) { - break; - } - - // Take top vertex from bucket and pop it - auto u = B[idx].front(); - B[idx].pop_front(); - - // Process all adjacents of extracted vertex 'u' and - // update their distanced if required. - for (const auto &i : (*cachedAdjMatrix)[u]) { - auto v = i.first; - int weight = 0; - if (i.second->isWeighted().has_value() && - i.second->isWeighted().value()) { - if (i.second->isDirected().has_value() && - i.second->isDirected().value()) { - shared> dw_edge = - std::static_pointer_cast>(i.second); - weight = (int)dw_edge->getWeight(); - } else if (i.second->isDirected().has_value() && - !i.second->isDirected().value()) { - shared> udw_edge = - std::static_pointer_cast>( - i.second); - weight = (int)udw_edge->getWeight(); - } else { - // ERROR it shouldn't never returned ( does not exist a Node - // Weighted and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - auto u_i = std::find_if( - dist.begin(), dist.end(), - [u](std::pair>, - std::pair>>> const &it) { - return (*u == *(it.first)); - }); - - auto v_i = std::find_if( - dist.begin(), dist.end(), - [v](std::pair>, - std::pair>>> const &it) { - return (*v == *(it.first)); - }); - long du = u_i->second.first; - long dv = v_i->second.first; - - // If there is shorted path to v through u. - if (dv > du + weight) { - // If dv is not INF then it must be in B[dv] - // bucket, so erase its entry using iterator - // in O(1) - if (dv != std::numeric_limits::max()) { - auto findIter = std::find(B[dv].begin(), B[dv].end(), dist[v].second); - B[dv].erase(findIter); - } - - // updating the distance - dist[v].first = du + weight; - dv = dist[v].first; - - // pushing vertex v into updated distance's bucket - B[dv].push_front(v); - - // storing updated iterator in dist[v].second - dist[v].second = *(B[dv].begin()); - } - } - } - for (const auto &dist_i : dist) { - result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; - } - result.success = true; - - return result; -} - -template -double Graph::fordFulkersonMaxFlow(const Node &source, - const Node &target) const { - if (!isDirectedGraph()) { - return -1; - } - double maxFlow = 0; - std::unordered_map>, shared>, nodeHash> - parent; - std::unordered_map< - shared>, - std::unordered_map>, double, nodeHash>, - nodeHash> - weightMap; - // build weight map - auto edgeSet = this->getEdgeSet(); - for (const auto &edge : edgeSet) { - // The Edge are all Directed at this point because is checked at the - // start - if (edge->isWeighted().value_or(false)) { - shared> dw_edge = - std::static_pointer_cast>(edge); - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - dw_edge->getWeight(); - } else { - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - 0; // No Weighted Edge are assumed to be 0 weigthed - } - } - - // Constuct iterators for source and target nodes in nodeSet - auto nodeSet = getNodeSet(); - auto source_node_ptr = *std::find_if( - nodeSet.begin(), nodeSet.end(), - [&source](auto node) { return node->getUserId() == source.getUserId(); }); - auto target_node_ptr = *std::find_if( - nodeSet.begin(), nodeSet.end(), - [&target](auto node) { return node->getUserId() == target.getUserId(); }); - - auto bfs_helper = [this, &source_node_ptr, &target_node_ptr, &parent, - &weightMap]() -> bool { - std::unordered_map>, bool, nodeHash> visited; - std::queue>> queue; - queue.push(source_node_ptr); - visited[source_node_ptr] = true; - parent[source_node_ptr] = nullptr; - while (!queue.empty()) { - auto u = queue.front(); - queue.pop(); - for (auto &v : weightMap[u]) { - if (!visited[v.first] && v.second > 0) { - queue.push(v.first); - visited[v.first] = true; - parent[v.first] = u; - } - } - } - - return (visited[target_node_ptr]); - }; - // Updating the residual values of edges - while (bfs_helper()) { - double pathFlow = std::numeric_limits::max(); - for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { - auto u = parent[v]; - pathFlow = std::min(pathFlow, weightMap[u][v]); - } - for (auto v = target_node_ptr; v != source_node_ptr; v = parent[v]) { - auto u = parent[v]; - weightMap[u][v] -= pathFlow; - weightMap[v][u] += pathFlow; - } - // Adding the path flows - maxFlow += pathFlow; - } - - return maxFlow; -} - -template -const std::vector> Graph::graph_slicing(const Node &start) const { - std::vector> result; - - auto nodeSet = Graph::getNodeSet(); - // check if start node in the graph - auto start_node_it = std::find_if( - nodeSet.begin(), nodeSet.end(), - [&start](auto node) { return node->getUserId() == start.getUserId(); }); - if (start_node_it == nodeSet.end()) { - return result; - } - std::vector> C = Graph::depth_first_search(start); - std::deque>> C1; // complement of C i.e. nodeSet - C - for (auto const &node : nodeSet) { - // from the set of all nodes, remove nodes that exist in C - if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { - return (*node == nodeC); - }) == C.end()) - C1.push_back(node); - } - - // For all nodes in C', apply DFS - // and get the list of reachable nodes and store in M - std::vector> M; - for (auto const &node : C1) { - std::vector> reachableNodes = Graph::depth_first_search(*node); - M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); - } - // removes nodes from C that are reachable from M. - for (const auto &nodeC : C) { - if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { - return (nodeM == nodeC); - }) == M.end()) - result.push_back(nodeC); - } - return result; -} - -template -int Graph::writeToFile(InputOutputFormat format, - const std::string &workingDir, - const std::string &OFileName, bool compress, - bool writeNodeFeat, bool writeEdgeWeight) const { - int result = 0; - - // Open streams and write - auto extSep = getExtenstionAndSeparator(format); - if (!extSep) { - std::cerr << "Unknown format\n"; - return -1; - } - auto &[extension, separator] = *extSep; - - std::ofstream ofileGraph; - std::ofstream ofileNodeFeat; - std::ofstream ofileEdgeWeight; - - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - ofileGraph.open(completePathToFileGraph); - if (!ofileGraph.is_open()) { - // ERROR File Not Open - return -1; - } - - if (writeNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - ofileNodeFeat.open(completePathToFileNodeFeat); - if (!ofileNodeFeat.is_open()) { - // ERROR File Not Open - return -1; - } - } - - if (writeEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - ofileEdgeWeight.open(completePathToFileEdgeWeight); - if (!ofileEdgeWeight.is_open()) { - // ERROR File Not Open - std::cout << "ERROR File Not Open" << std::endl; - return -1; - } - } - - writeGraphToStream(ofileGraph, ofileNodeFeat, ofileEdgeWeight, separator, - writeNodeFeat, writeEdgeWeight); - - // Cleanup from writing - ofileGraph.close(); - if (writeNodeFeat) ofileNodeFeat.close(); - if (writeEdgeWeight) ofileEdgeWeight.close(); - -#ifdef WITH_COMPRESSION - if (result == 0 && compress) { - auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, - &writeEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = compressFile(completePathToFileGraph, - completePathToFileGraphCompressed); - if (_result == 0) { - _result = remove(completePathToFileGraph.c_str()); - } - if (_result == 0) { - if (writeNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; - _result = compressFile(completePathToFileNodeFeat, - completePathToFileNodeFeatCompressed); - if (_result == 0) { - _result = remove(completePathToFileNodeFeat.c_str()); - } - } - } - if (_result == 0) { - if (writeEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; - _result = compressFile(completePathToFileEdgeWeight, - completePathToFileEdgeWeightCompressed); - if (_result == 0) { - _result = remove(completePathToFileEdgeWeight.c_str()); - } - } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - auto result = _compress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - auto result = _compress(".tsv"); - } else { - // OUTPUT FORMAT NOT RECOGNIZED - result = -1; - } - } -#endif - - return result; -} - -template -int Graph::readFromFile(InputOutputFormat format, - const std::string &workingDir, - const std::string &OFileName, bool compress, - bool readNodeFeat, bool readEdgeWeight) { - int result = 0; - -#ifdef WITH_COMPRESSION - if (compress) { - auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, - &readEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = decompressFile(completePathToFileGraphCompressed, - completePathToFileGraph); - if (_result == 0) { - if (readNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; - _result = decompressFile(completePathToFileNodeFeatCompressed, - completePathToFileNodeFeat); - } - } - if (_result == 0) { - if (readEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; - _result = decompressFile(completePathToFileEdgeWeightCompressed, - completePathToFileEdgeWeight); - } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - result = decompress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - result = decompress(".tsv"); - } else { - // INPUT FORMAT NOT RECOGNIZED - result = -1; - } - - if (result != 0) { - return result; - } - } -#endif - // Open streams and read - auto extSep = getExtenstionAndSeparator(format); - if (!extSep) { - std::cerr << "Unknown format\n"; - return -1; - } - auto &[extension, separator] = *extSep; - - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileNodeFeat; - std::string completePathToFileEdgeWeight; - - std::ifstream ifileGraph; - std::ifstream ifileNodeFeat; - std::ifstream ifileEdgeWeight; - - ifileGraph.open(completePathToFileGraph); - if (!ifileGraph.is_open()) { - // ERROR File Not Open - // std::cout << "ERROR File Not Open : " << completePathToFileGraph << - // std::endl; - return -1; - } - ifileGraph.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); - - if (readNodeFeat) { - completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - ifileNodeFeat.open(completePathToFileNodeFeat); - if (!ifileNodeFeat.is_open()) { - // ERROR File Not Open - // std::cout << "ERROR File Not Open" << std::endl; - return -1; - } - ifileNodeFeat.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); - } - - if (readEdgeWeight) { - completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - ifileEdgeWeight.open(completePathToFileEdgeWeight); - if (!ifileEdgeWeight.is_open()) { - // ERROR File Not Open - // std::cout << "ERROR File Not Open" << std::endl; - return -1; - } - ifileEdgeWeight.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); - } - - readGraphFromStream(ifileGraph, ifileNodeFeat, ifileEdgeWeight, readNodeFeat, - readEdgeWeight); - - // Cleanup - ifileGraph.close(); -#ifdef WITH_COMPRESSION - if (compress) remove(completePathToFileGraph.c_str()); -#endif - - if (readNodeFeat) { - ifileNodeFeat.close(); -#ifdef WITH_COMPRESSION - if (compress) remove(completePathToFileNodeFeat.c_str()); -#endif - } - - if (readEdgeWeight) { - ifileEdgeWeight.close(); -#ifdef WITH_COMPRESSION - if (compress) remove(completePathToFileEdgeWeight.c_str()); -#endif - } - - return result; -} - -template -int Graph::writeToDotFile(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName) const { - return writeToDot(workingDir, OFileName, graphName); -} - -template -int Graph::writeToMTXFile(const std::string &workingDir, - const std::string &OFileName, - char delimitier) const { - // Get the full path and open the file - const std::string completePathToFileGraph = - workingDir + '/' + OFileName + ".mtx"; - std::ofstream iFile(completePathToFileGraph); - - // Write the header of the file - std::string header = "%%MatrixMarket graph"; - // Check if the adjacency matrix is symmetric, i.e., if all the edges are - // undirected - bool symmetric = !std::any_of(edgeSet.begin(), edgeSet.end(), [](auto edge) { - return (edge->isDirected().has_value() && edge->isDirected().value()); - }); - // Write in the header whether the adj matrix is symmetric or not - if (symmetric) { - header += " symmetric\n"; - } else { - header += '\n'; - } - iFile << header; - - // Write the line containing the number of nodes and edges - const std::string firstLine = - std::to_string(getNodeSet().size()) + delimitier + - std::to_string(getNodeSet().size()) + delimitier + - std::to_string(getEdgeSet().size()) + '\n'; - iFile << firstLine; - - // Construct the edges - for (const auto &edgeIt : edgeSet) { - std::string line; - line += edgeIt->getNodePair().first->getUserId() + delimitier; - line += edgeIt->getNodePair().second->getUserId() + delimitier; - if (edgeIt->isWeighted().has_value() && edgeIt->isWeighted().value()) { - line += std::to_string(edgeIt->isWeighted().value()) + '\n'; - } else { - line += std::to_string(1.) + '\n'; - } - iFile << line; - } - - iFile.close(); - return 0; -} - -template -int Graph::readFromDotFile(const std::string &workingDir, - const std::string &fileName) { - return readFromDot(workingDir, fileName); -} - -template -int Graph::readFromMTXFile(const std::string &workingDir, - const std::string &fileName) { - // Define the edge maps - std::unordered_map> - edgeMap; - std::unordered_map nodeFeatMap; - std::unordered_map edgeDirectedMap; - std::unordered_map edgeWeightMap; - - // Get full path to the file and open it - const std::string completePathToFileGraph = - workingDir + '/' + fileName + ".mtx"; - std::ifstream iFile(completePathToFileGraph); - // Check that the file is open - if (!iFile.is_open()) { - return -1; - } - - // Define the number of columns and rows in the matrix - int n_cols, n_rows; - int n_edges; - bool undirected = false; - - // From the first line of the file read the number of rows, columns and edges - std::string row_content; - getline(iFile, row_content); - if (row_content.find("symmetric") != std::string::npos) { - undirected = true; - } - - // Get rid of any commented lines between the header and the size line - while (row_content.find('%') != std::string::npos) { - getline(iFile, row_content); - } - - // From the size line of the file read the number of rows, columns and edges - std::stringstream header_stream(row_content); - std::string value; - getline(header_stream, value, ' '); - n_rows = std::stoi(value); - getline(header_stream, value, ' '); - n_cols = std::stoi(value); - getline(header_stream, value, ' '); - n_edges = std::stoi(value); - - // Since the matrix represents the adjacency matrix, it must be square - if (n_rows != n_cols) { - return -1; - } - - // Read the content of each line - std::string node1; - std::string node2; - std::string edge_weight; - CXXGraph::id_t edge_id = 0; - while (getline(iFile, row_content)) { - std::stringstream row_stream(row_content); - - // Read the content of the node ids and the weight into strings - getline(row_stream, node1, ' '); - getline(row_stream, node2, ' '); - getline(row_stream, edge_weight); - - edgeMap[edge_id] = std::pair(node1, node2); - edgeWeightMap[edge_id] = std::stod(edge_weight); - edgeDirectedMap[edge_id] = !undirected; - - // If the edge is a self-link, it must be undirected - if (node1 == node2) { - edgeDirectedMap[edge_id] = false; - } - - // Increase the edge id - ++edge_id; - } - - if (n_edges != edgeMap.size()) { - std::cout << "Error: The number of edges does not match the value provided " - "in the size line.\n"; - return -1; - } - - iFile.close(); - recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); - return 0; -} - -template -PartitionMap Graph::partitionGraph( - const Partitioning::PartitionAlgorithm algorithm, - const unsigned int numberOfPartitions, const double param1, - const double param2, const double param3, - const unsigned int numberOfThreads) const { - PartitionMap partitionMap; - Partitioning::Globals globals(numberOfPartitions, algorithm, param1, param2, - param3, numberOfThreads); - auto edgeSet_ptr = make_shared>(getEdgeSet()); - globals.edgeCardinality = edgeSet_ptr->size(); - globals.vertexCardinality = this->getNodeSet().size(); - Partitioning::Partitioner partitioner(edgeSet_ptr, globals); - Partitioning::CoordinatedPartitionState partitionState = - partitioner.performCoordinatedPartition(); - partitionMap = partitionState.getPartitionMap(); - return partitionMap; -} - -template -std::ostream &operator<<(std::ostream &os, const Graph &graph) { - os << "Graph:\n"; - auto edgeList = graph.getEdgeSet(); - auto it = edgeList.begin(); - for (it; it != edgeList.end(); ++it) { - if (!(*it)->isDirected().has_value() && !(*it)->isWeighted().has_value()) { - // Edge Case - os << **it << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - ((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) - << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - !((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) << "\n"; - } else if (!((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - ((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) - << "\n"; - } else if (!((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - !((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << *std::static_pointer_cast>(*it) << "\n"; - } else { - os << *it << "\n"; - } - } - return os; -} - -template -std::ostream &operator<<(std::ostream &os, const AdjacencyMatrix &adj) { - os << "Adjacency Matrix:\n"; - unsigned long max_column = 0; - for (const auto &it : adj) { - if (it.second.size() > max_column) { - max_column = (unsigned long)it.second.size(); - } - } - if (max_column == 0) { - os << "ERROR in Print\n"; - return os; - } else { - os << "|--|"; - for (unsigned long i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - for (const auto &it : adj) { - os << "|N" << it.first->getId() << "|"; - for (const auto &it2 : it.second) { - os << "N" << it2.first->getId() << ",E" << it2.second->getId() << "|"; - } - os << "\n|--|"; - for (unsigned long i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - } - } - return os; -} - -} // namespace CXXGraph -#endif // __CXXGRAPH_GRAPH_H__ diff --git a/include/CXXGraph/Graph/Graph_decl.h b/include/CXXGraph/Graph/Graph_decl.h new file mode 100644 index 000000000..01f8f48b6 --- /dev/null +++ b/include/CXXGraph/Graph/Graph_decl.h @@ -0,0 +1,859 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_GRAPH_DECL_H__ +#define __CXXGRAPH_GRAPH_DECL_H__ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Node/Node.h" +#include "CXXGraph/Utility/TypeTraits.hpp" +#include "CXXGraph/Utility/Typedef.hpp" + +#ifdef WITH_COMPRESSION +#include +#endif + +namespace CXXGraph { +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +template +using T_EdgeSet = std::unordered_set>, edgeHash>; + +template +using T_NodeSet = std::unordered_set>, nodeHash>; + + +template +class Graph; + +template +std::ostream &operator<<(std::ostream &o, const Graph &graph); +template +std::ostream &operator<<(std::ostream &o, const AdjacencyMatrix &adj); + +/// Class that implement the Graph. ( This class is not Thread Safe ) +template +class Graph { + private: + T_EdgeSet edgeSet = {}; + T_NodeSet isolatedNodesSet = {}; + + shared> cachedAdjMatrix; + shared> cachedDegreeMatrix; + shared> cachedLaplacianMatrix; + shared> cachedTransitionMatrix; + // Private non-const getter for the set of nodes + std::unordered_set>, nodeHash> nodeSet(); + + std::optional> getExtenstionAndSeparator( + InputOutputFormat format) const; + void writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, + std::ostream &oEdgeWeight, const char &sep, + bool writeNodeFeat, bool writeEdgeWeight) const; + void readGraphFromStream(std::istream &iGraph, std::istream &iNodeFeat, + std::istream &iEdgeWeight, bool readNodeFeat, + bool readEdgeWeight); + int writeToDot(const std::string &workingDir, const std::string &OFileName, + const std::string &graphName) const; + int readFromDot(const std::string &workingDir, const std::string &fileName); + void recreateGraph( + std::unordered_map> + &edgeMap, + std::unordered_map &edgeDirectedMap, + std::unordered_map &nodeFeatMap, + std::unordered_map &edgeWeightMap); + +#ifdef WITH_COMPRESSION + int compressFile(const std::string &inputFile, + const std::string &outputFile) const; + int decompressFile(const std::string &inputFile, + const std::string &outputFile) const; +#endif + + public: + Graph(); + Graph(const T_EdgeSet &edgeSet); + virtual ~Graph() = default; + /** + * \brief + * Function that return the Edge set of the Graph + * Note: No Thread Safe + * + * @returns a list of Edges of the graph + * + */ + virtual const T_EdgeSet &getEdgeSet() const; + /** + * \brief + * Function set the Edge Set of the Graph + * Note: No Thread Safe + * + * @param edgeSet The Edge Set + * + */ + virtual void setEdgeSet(const T_EdgeSet &edgeSet); + /** + * \brief + * Function add an Edge to the Graph Edge Set + * First check if a pointer to a node with the same userId has + * already been added, and if not add it + * Note: No Thread Safe + * + * @param edge The Edge to insert + * + */ + virtual void addEdge(const Edge *edge); + /** + * \brief + * Function add an Edge to the Graph Edge Set + * First check if a pointer to a node with the same userId has + * already been added, and if not add it + * Note: No Thread Safe + * + * @param edge The Edge to insert + * + */ + virtual void addEdge(shared> edge); + /** + * \brief + * Function that adds any number of Edges to the Graph Edge set + * Note: This is the overload needed to terminate the + * recursion + * + * @param None + * + */ + template + void addEdges(); + /** + * \brief + * Function that adds any number of Edges to the Graph Edge set + * + * @param Raw pointers or shared pointers to the Edges + * + */ + template + std::enable_if && (is_edge_ptr_v && ...), void> + addEdges(T1 edge, Tn... edges); + /** + * \brief + * Function to add a Node to the Graph Node Set + * Note: No Thread Safe + * + * @param pointer to the node + * + */ + virtual void addNode(const Node *node); + /** + * \brief + * Function to add a Node to the Graph Node Set + * Note: No Thread Safe + * + * @param shared pointer to the node + * + */ + virtual void addNode(shared> node); + /** + * \brief + * Function that adds any number of Nodes to the Graph Node set + * Note: This overload is needed to terminate the recursion + * + * @param None + * + */ + template + void addNodes(); + /** + * \brief + * Function that adds any number of Nodes to the Graph Node set + * + * @param Raw pointers or shared pointers to the Edges + * + */ + template + std::enable_if && (is_node_ptr_v && ...), void> + addNodes(T1 node, Tn... nodes); + /** + * \brief + * Function remove an Edge from the Graph Edge Set + * Note: No Thread Safe + * + * @param edgeId The Edge Id to remove + * + */ + virtual void removeEdge(const CXXGraph::id_t edgeId); + /** + * \brief + * Function to remove a Node from the Graph Node Set + * Note: No Thread Safe + * + * @param edgeId The Edge Id to remove + * + */ + virtual void removeNode(const std::string &nodeUserId); + /** + * \brief + * Finds the given edge defined by v1 and v2 within the graph. + * + * @param v1 The first vertex. + * @param v2 The second vertex. + * @param id The edge id if the edge is found. Otherwise set to 0. + * @return True if the edge exists in the graph. + */ + virtual bool findEdge(const Node *v1, const Node *v2, + CXXGraph::id_t &id) const; + /** + * \brief + * Overload of findEdge which takes shared pointers as parameters + * + * @param v1 The first vertex. + * @param v2 The second vertex. + * @param id The edge id if the edge is found. Otherwise set to 0. + * @return True if the edge exists in the graph. + */ + virtual bool findEdge(shared> v1, shared> v2, + CXXGraph::id_t &id) const; + /** + * \brief + * Function that return the Node Set of the Graph + * Note: No Thread Safe + * + * @returns a list of Nodes of the graph + * + */ + virtual const T_NodeSet getNodeSet() const; + /** + * \brief + * Function that return the Set of isolated nodes + * in the Graph + * Note: No Thread Safe + * + * @returns a list of Nodes of the graph + * + */ + virtual const T_NodeSet getIsolatedNodeSet() const; + /** + * \brief + * Function that sets the data contained in a node + * + * @param nodeUserId The userId string of the node whose data is to be changes + * @param data The new value for the node data + * + */ + virtual void setNodeData(const std::string &nodeUserId, T data); + /** + * \brief + * Function that sets the data contained in every node of the graph + * + * @param dataMap Map of the userId of every node with its new data value + * + */ + virtual void setNodeData(std::map &dataMap); + /** + * \brief + * Function that return an Edge with specific ID if Exist in the Graph + * Note: No Thread Safe + * + * @param edgeId The Edge Id to return + * @returns the Edge if exist + * + */ + virtual const std::optional>> getEdge( + const CXXGraph::id_t edgeId) const; + /** + * \brief + * Function that return a Node with specific ID if Exist in the Graph + * Note: No Thread Safe + * + * @param nodeId The Node Id to return + * @returns the Node if exist + * + */ + virtual const std::optional>> getNode( + const std::string &nodeUserId) const; + /** + * @brief This function generate a list of adjacency matrix with every element + * of the matrix contain the node where is directed the link and the Edge + * corrispondent to the link + * Note: No Thread Safe + */ + virtual shared> getAdjMatrix() const; + + virtual void cacheAdjMatrix(); + /** + * @brief This function generates a list of the degree matrix with every + * element of the matrix containing the node where the link is directed and + * the corresponding edge to the link. Note: No Thread Safe + */ + virtual shared> getDegreeMatrix() const; + + virtual void cacheDegreeMatrix(); + /** + * @brief This function generates a list of the Laplacian matrix with every + * element of the matrix containing the node connected to the current node and + * the corresponding edge to the link. Note: No Thread Safe + */ + virtual shared> getLaplacianMatrix() const; + + virtual void cacheLaplacianMatrix(); + /** + * @brief This function generates a list of the transition matrix with every + * element of the matrix containing the node that can be transitioned to from + * the current node and the probability of the transition. Note: No Thread + * Safe + */ + virtual shared> getTransitionMatrix() const; + + virtual void cacheTransitionMatrix(); + /** + * \brief This function generates a set of nodes linked to the provided node + * in a directed graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + outNeighbors(const Node *node) const; + /** + * \brief This function generates a set of nodes linked to the provided node + * in a directed graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + outNeighbors(shared> node) const; + /** + * \brief This function generates a set of nodes linked to the provided node + * in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + inOutNeighbors(const Node *node) const; + /** + * \brief + * \brief This function generates a set of nodes linked to the provided node + * in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, nodeHash> + inOutNeighbors(shared> node) const; + /** + * \brief + * \brief This function generates a set of Edges going out of a node + * in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> outEdges( + const Node *node) const; + /** + * \brief + * \brief This function generates a set of Edges going out of a node + * in any graph + * + * @param Shared pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> outEdges( + shared> node) const; + /** + * \brief + * \brief This function generates a set of Edges coming in or going out of + * a node in any graph + * + * @param Pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> + inOutEdges(const Node *node) const; + /** + * \brief + * \brief This function generates a set of Edges coming in or going out of + * a node in any graph + * + * @param Shared pointer to the node + * + */ + virtual const std::unordered_set>, edgeHash> + inOutEdges(shared> node) const; + /** + * @brief This function finds the subset of given a nodeId + * Subset is stored in a map where keys are the hash-id of the node & values + * is the subset. + * @param subset query subset, we want to find target in this subset + * @param elem elem that we wish to find in the subset + * + * @return parent node of elem + * Note: No Thread Safe + */ + virtual CXXGraph::id_t setFind(std::unordered_map *, + const CXXGraph::id_t elem) const; + /** + * @brief This function finds the subset of given a nodeId + * Subset is stored in a map where keys are the hash-id of the node & values + * is the subset. + * @param shared pointer to subset query subset, we want to find target in + * this subset + * @param elem elem that we wish to find in the subset + * + * @return parent node of elem + * Note: No Thread Safe + */ + virtual CXXGraph::id_t setFind( + shared>, + const CXXGraph::id_t elem) const; + /** + * @brief This function modifies the original subset array + * such that it the union of two sets a and b + * @param subset original subset is modified to obtain union of a & b + * @param a parent id of set1 + * @param b parent id of set2 + * NOTE: Original subset is no longer available after union. + * Note: No Thread Safe + */ + virtual void setUnion(std::unordered_map *, + const CXXGraph::id_t set1, + const CXXGraph::id_t elem2) const; + /** + * @brief This function modifies the original subset array + * such that it the union of two sets a and b + * @param subset original subset is modified to obtain union of a & b + * @param a parent id of set1 + * @param b parent id of set2 + * NOTE: Original subset is no longer available after union. + * Note: No Thread Safe + */ + virtual void setUnion(shared>, + const CXXGraph::id_t set1, + const CXXGraph::id_t elem2) const; + /** + * @brief This function finds the eulerian path of a directed graph using + * hierholzers algorithm + * + * @return a vector containing nodes in eulerian path + * Note: No Thread Safe + */ + virtual std::shared_ptr>> eulerianPath() const; + /** + * @brief Function runs the dijkstra algorithm for some source node and + * target node in the graph and returns the shortest distance of target + * from the source. + * Note: No Thread Safe + * + * @param source source vertex + * @param target target vertex + * + * @return shortest distance if target is reachable from source else ERROR in + * case if target is not reachable from source or there is error in the + * computation. + */ + virtual const DijkstraResult dijkstra(const Node &source, + const Node &target) const; + /** + * @brief This function runs the tarjan algorithm and returns different types + * of results depending on the input parameter typeMask. + * + * @param typeMask each bit of typeMask within valid range represents a kind + * of results should be returned. + * + * Note: No Thread Safe + * + * @return The types of return include strongly connected components + * (only for directed graphs) and cut vertices、 bridges、edge + * biconnected components and vertice biconnected components + * (only for undirected graphs). + */ + virtual const TarjanResult tarjan(const unsigned int typeMask) const; + /** + * @brief Function runs the bellman-ford algorithm for some source node and + * target node in the graph and returns the shortest distance of target + * from the source. It can also detect if a negative cycle exists in the + * graph. Note: No Thread Safe + * + * @param source source vertex + * @param target target vertex + * + * @return shortest distance if target is reachable from source else ERROR in + * case if target is not reachable from source. If there is no error then also + * returns if the graph contains a negative cycle. + */ + virtual const BellmanFordResult bellmanford(const Node &source, + const Node &target) const; + /** + * @brief This function computes the transitive reduction of the graph, + * returning a graph with the property of transitive closure satisfied. It + * removes the "short-circuit" paths from a graph, leaving only the longest + * paths. Commonly used to remove duplicate edges among nodes that do not pass + * through the entire graph. + * @return A copy of the current graph with the transitive closure property + * satisfied. + * + */ + virtual const Graph transitiveReduction() const; + /** + * @brief Function runs the floyd-warshall algorithm and returns the shortest + * distance of all pair of nodes. It can also detect if a negative cycle + * exists in the graph. Note: No Thread Safe + * @return a map whose keys are node ids and values are the shortest distance. + * If there is no error then also returns if the graph contains a negative + * cycle. + */ + virtual const FWResult floydWarshall() const; + /** + * @brief Function runs the prim algorithm and returns the minimum spanning + * tree if the graph is undirected. Note: No Thread Safe + * @return a vector containing id of nodes in minimum spanning tree & cost of + * MST + */ + virtual const MstResult prim() const; + /** + * @brief Function runs the boruvka algorithm and returns the minimum spanning + * tree & cost if the graph is undirected. Note: No Thread Safe + * @return struct of type MstResult with following fields + * success: true if algorithm completed successfully ELSE false + * mst: vector containing id of nodes in minimum spanning tree & cost of MST + * mstCost: Cost of MST + * errorMessage: "" if no error ELSE report the encountered error + */ + virtual const MstResult boruvka() const; + /** + * @brief Function runs the kruskal algorithm and returns the minimum spanning + * tree if the graph is undirected. Note: No Thread Safe + * @return struct of type MstResult with following fields + * success: true if algorithm completed successfully ELSE false + * mst: vector containing id of nodes in minimum spanning tree & cost of MST + * mstCost: Cost of MST + * errorMessage: "" if no error ELSE report the encountered error + */ + virtual const MstResult kruskal() const; + /** + * \brief + * Function runs the best first search algorithm over the graph + * using an evaluation function to decide which adjacent node is + * most promising to explore + * Note: No Thread Safe + * + * @param source source node + * @param target target node + * @returns a struct with a vector of Nodes if target is reachable else ERROR + * in case if target is not reachable or there is an error in the computation. + * + */ + virtual BestFirstSearchResult best_first_search( + const Node &source, const Node &target) const; + /** + * \brief + * Function performs the breadth first search algorithm over the graph + * Note: No Thread Safe + * + * @param start Node from where traversing starts + * @returns a vector of Node indicating which Node were visited during the + * search. + * + */ + virtual const std::vector> breadth_first_search( + const Node &start) const; + /** + * \brief + * The multithreaded version of breadth_first_search + * It turns out to be two indepentent functions because of implemntation + * differences + * + * @param start Node from where traversing starts + * @param num_threads number of threads + * @returns a vector of Node indicating which Node were visited during the + * search. + * + */ + virtual const std::vector> concurrency_breadth_first_search( + const Node &start, size_t num_threads) const; + /** + * \brief + * Function performs the depth first search algorithm over the graph + * Note: No Thread Safe + * + * @param start Node from where traversing starts + * @returns a vector of Node indicating which Node were visited during the + * search. + * + */ + virtual const std::vector> depth_first_search( + const Node &start) const; + + /** + * \brief + * This function uses DFS to check for cycle in the graph. + * Pay Attention, this function work only with directed Graph + * Note: No Thread Safe + * + * @return true if a cycle is detected, else false. ( false is returned also + * if the graph in indirected) + */ + virtual bool isCyclicDirectedGraphDFS() const; + + /** + * \brief + * This function uses BFS to check for cycle in the graph. + * Pay Attention, this function work only with directed Graph + * Note: No Thread Safe + * + * @return true if a cycle is detected, else false. ( false is returned also + * if the graph in indirected) + */ + virtual bool isCyclicDirectedGraphBFS() const; + + /** + * @brief + * This function checks if the given set of edges + * forms a cycle or not using union-find method. + * + * @return true if a cycle is detected, else false + */ + virtual bool containsCycle(const T_EdgeSet *) const; + /** + * @brief + * This function checks if the given set of edges + * forms a cycle or not using union-find method. + * + * @return true if a cycle is detected, else false + */ + virtual bool containsCycle(shared>) const; + /** + * @brief + * This function checks if the given Subset + * forms a cycle or not using union-find method. + * + * @return true if a cycle is detected, else false + */ + virtual bool containsCycle( + shared> edgeSet, + shared>) const; + + /** + * \brief + * This function checks if a graph is directed + * Note: No Thread Safe + * + * @return true if the graph is directed, else false. + */ + virtual bool isDirectedGraph() const; + + /** + * \brief + * This function checks if a graph is undirected + * Note: No Thread Safe + * + * @return true if the graph is undirected, else false. + */ + virtual bool isUndirectedGraph() const; + + /** + * @brief This function reverse the direction of the edges in a directed graph + */ + virtual void reverseDirectedGraph(); + + /** + * @brief This function checks if the graph is connected or not + * Applicable for Undirected Graph, for Directed Graph use the + * isStronglyConnectedGraph() function + * + * @return true if the graph is connected + * @return false otherwise + */ + virtual bool isConnectedGraph() const; + + /** + * @brief This function checks if the graph is strongly connected or not + * Applicable for Directed Graph, for Undirected Graph use the + * isConnectedGraph() function + * + * @return true if the graph is connected + * @return false otherwise + */ + virtual bool isStronglyConnectedGraph() const; + + /** + * @brief This function sort nodes in topological order. + * Applicable for Directed Acyclic Graph + * + * @return a struct with a vector of Nodes ordered topologically else ERROR in + * case of undirected or cyclic graph + */ + virtual TopoSortResult topologicalSort() const; + + /** + * @brief This function sort nodes in topological order using kahn's algorithm + * Applicable for Directed Acyclic Graph + * + * @return a struct with a vector of Nodes ordered topologically else ERROR in + * case of undirected or cyclic graph + */ + virtual TopoSortResult kahn() const; + + /** + * \brief + * This function performs performs the kosaraju algorthm on the graph to find + the strongly connected components. + * + * Mathematical definition of the problem: + * A strongly connected component (SCC) of a directed graph is a maximal + strongly connected subgraph. + + * Note: No Thread Safe + * @return a vector of vector of strongly connected components. + */ + virtual SCCResult kosaraju() const; + + /** + * \brief + * This function performs Graph Slicing based on connectivity + * + * Mathematical definition of the problem: + * + * Let G be the set of nodes in a graph and n be a given node in that set. + * Let C be the non-strict subset of G containing both n and all nodes + reachable + * from n, and let C' be its complement. There's a third set M, which is the + * non-strict subset of C containing all nodes that are reachable from any node + in C'. + * The problem consists of finding all nodes that belong to C but not to M. + + * Note: No Thread Safe + * @param start Node from where traversing starts + * @return a vector of nodes that belong to C but not to M. + */ + virtual const std::vector> graph_slicing(const Node &start) const; + + /** + * @brief Function runs the Dial algorithm (Optimized Dijkstra for small + * range weights) for some source node and target node in the graph and + * returns the shortest distance of target from the source. Note: No Thread + * Safe + * + * @param source source vertex + * @param maxWeight maximum weight of the edge + * + * @return shortest distance for all nodes reachable from source else ERROR in + * case there is error in the computation. + */ + virtual const DialResult dial(const Node &source, int maxWeight) const; + + /** + * @brief Function runs the Ford-Fulkerson algorithm for some source node and + * target node in the graph and returns the maximum flow of the graph + * + * @param source source vertex + * @param target target vertex + * @return double Max-Flow value or -1 in case of error + */ + virtual double fordFulkersonMaxFlow(const Node &source, + const Node &target) const; + + /** + * \brief + * This function writes the graph to an output file + * Note: Not threadsafe + * + * @param format The output format of the file + * @param workingDir The parent directory of the output file + * @param OFileName The output filename + * @param compress Enables compression (requires zlib) + * @param writeNodeFeat Indicates if export also Node Features + * @param writeEdgeWeight Indicates if export also Edge Weights + * @return 0 if OK, else return a negative value + */ + virtual int writeToFile( + InputOutputFormat format = InputOutputFormat::STANDARD_CSV, + const std::string &workingDir = ".", + const std::string &OFileName = "graph", bool compress = false, + bool writeNodeFeat = false, bool writeEdgeWeight = false) const; + + virtual int writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const; + + virtual int writeToMTXFile(const std::string &workingDir, + const std::string &OFileName, char delimier) const; + + /** + * \brief + * This function reads the graph from an input file + * Note: Not threadsafe + * + * @param format The input format of the file + * @param workingDir The parent directory of the input + * file + * @param OFileName The input filename + * @param compress Enables compression (requires zlib) + * @param readNodeFeat Indicates if import also Node Features + * @param readEdgeWeight Indicates if import also Edge Weights + * @return 0 if OK, else return a negative value + */ + virtual int readFromFile( + InputOutputFormat format = InputOutputFormat::STANDARD_CSV, + const std::string &workingDir = ".", + const std::string &OFileName = "graph", bool compress = false, + bool readNodeFeat = false, bool readEdgeWeight = false); + + virtual int readFromDotFile(const std::string &workingDir, + const std::string &fileName); + + virtual int readFromMTXFile(const std::string &workingDir, + const std::string &fileName); + + + + friend std::ostream &operator<< <>(std::ostream &os, const Graph &graph); + friend std::ostream &operator<< <>(std::ostream &os, + const AdjacencyMatrix &adj); +}; + +} // namespace CXXGraph +#endif // __CXXGRAPH_GRAPH_DECL_H__ diff --git a/include/CXXGraph/Graph/Graph_impl.hpp b/include/CXXGraph/Graph/Graph_impl.hpp new file mode 100644 index 000000000..cbc72b5fe --- /dev/null +++ b/include/CXXGraph/Graph/Graph_impl.hpp @@ -0,0 +1,880 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_GRAPH_IMPL_H__ +#define __CXXGRAPH_GRAPH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" +#include "CXXGraph/Utility/ConstString.hpp" +#include + +namespace CXXGraph { + +using std::make_shared; +using std::make_unique; + +template +Graph::Graph() { + /* Caching the adjacency matrix */ + cacheAdjMatrix(); + cacheDegreeMatrix(); + cacheLaplacianMatrix(); + cacheTransitionMatrix(); +} + +template +Graph::Graph(const T_EdgeSet &edgeSet) { + for (auto edgeIt : edgeSet) { + this->edgeSet.insert(edgeIt); + } + /* Caching the adjacency matrix */ + cacheAdjMatrix(); + cacheDegreeMatrix(); + cacheLaplacianMatrix(); + cacheTransitionMatrix(); +} + +template +const T_EdgeSet &Graph::getEdgeSet() const { + return edgeSet; +} + +template +void Graph::setEdgeSet(const T_EdgeSet &edgeSet) { + this->edgeSet.clear(); + for (auto edgeIt : edgeSet) { + this->edgeSet.insert(edgeIt); + } + /* Caching the adjacency matrix */ + cacheAdjMatrix(); + cacheDegreeMatrix(); + cacheLaplacianMatrix(); +} + +template +void Graph::addEdge(const Edge *edge) { + if (edge->isDirected().has_value() && edge->isDirected().value()) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edge_shared = make_shared>( + *dynamic_cast *>(edge)); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + } else { + auto edge_shared = make_shared>(*edge); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> 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>( + *dynamic_cast *>(edge)); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge_shared->getNodePair().first, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( + std::move(elem1)); + } else { + auto edge_shared = make_shared>(*edge); + this->edgeSet.insert(edge_shared); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge_shared->getNodePair().first, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( + std::move(elem1)); + } + } +} + +template +void Graph::addEdge(shared> 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>> elem = { + edge.get()->getNodePair().second, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( + std::move(elem)); + } else { + std::pair>, shared>> elem = { + edge.get()->getNodePair().second, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge.get()->getNodePair().first, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().second].push_back( + std::move(elem1)); + } +} + +template +template +void Graph::addEdges() { + return; +} + +template +template +std::enable_if && (is_edge_ptr_v && ...), void> +Graph::addEdges(T1 edge, Tn... edges) { + addEdge(edge); + addEdges(edges...); +} + +template +void Graph::addNode(const Node *node) { + auto node_shared = make_shared>(*node); + this->isolatedNodesSet.insert(node_shared); +} + +template +void Graph::addNode(shared> node) { + this->isolatedNodesSet.insert(node); +} + +template +template +void Graph::addNodes() { + return; +} + +template +template +std::enable_if && (is_node_ptr_v && ...), void> +Graph::addNodes(T1 node, Tn... nodes) { + addNode(node); + addNodes(nodes...); +} + +template +void Graph::removeEdge(const CXXGraph::id_t edgeId) { + auto edgeOpt = Graph::getEdge(edgeId); + if (edgeOpt.has_value()) { + /* + edgeSet.erase(std::find_if(this->edgeSet.begin(), this->edgeSet.end(), + [edgeOpt](const Edge *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); + } + } +} + +template +void Graph::removeNode(const std::string &nodeUserId) { + auto nodeOpt = getNode(nodeUserId); + auto isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value()); + + if (nodeOpt.has_value() && isolatedNodeIt != isolatedNodesSet.end()) { + // The node is isolated + isolatedNodesSet.erase(isolatedNodeIt); + } else if (nodeOpt.has_value()) { + // The node is not isolated + // Remove the edges containing the node + auto edgeset = edgeSet; + for (auto edgeIt : edgeset) { + if (edgeIt->getNodePair().first->getUserId() == nodeUserId || + edgeIt->getNodePair().second->getUserId() == nodeUserId) { + this->removeEdge(edgeIt->getId()); + } + } + } +} + +template +bool Graph::findEdge(const Node *v1, const Node *v2, + CXXGraph::id_t &id) const { + auto v1_shared = make_shared>(*v1); + auto v2_shared = make_shared>(*v2); + + return findEdge(v1_shared, v2_shared, id); +} + +template +bool Graph::findEdge(shared> v1, shared> v2, + CXXGraph::id_t &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 + if (cachedAdjMatrix.get() != NULL && cachedAdjMatrix->size() != 0) { + /* 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; + } + } + } + id = 0; + return false; +} + +template +const T_NodeSet Graph::getNodeSet() const { + T_NodeSet nodeSet; + + for (const auto &edgeSetIt : edgeSet) { + nodeSet.insert(edgeSetIt->getNodePair().first); + nodeSet.insert(edgeSetIt->getNodePair().second); + } + // Merge with the isolated nodes + nodeSet.insert(this->isolatedNodesSet.begin(), this->isolatedNodesSet.end()); + + return nodeSet; +} + +template +const T_NodeSet Graph::getIsolatedNodeSet() const { + return isolatedNodesSet; +} + +template +void Graph::setNodeData(const std::string &nodeUserId, T data) { + auto nodeSet = this->nodeSet(); + auto nodeIt = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&nodeUserId](auto node) { return node->getUserId() == nodeUserId; }); + std::const_pointer_cast>(*nodeIt)->setData(std::move(data)); +} + +template +void Graph::setNodeData(std::map &dataMap) { + // Construct the set of all the nodes in the graph + for (auto &nodeSetIt : this->nodeSet()) { + nodeSetIt->setData(std::move(dataMap[nodeSetIt->getUserId()])); + } +} + +template +const std::optional>> Graph::getEdge( + const CXXGraph::id_t edgeId) const { + for (const auto &it : edgeSet) { + if (it->getId() == edgeId) { + return it; + } + } + + return std::nullopt; +} + +template +const std::optional>> Graph::getNode( + const std::string &nodeUserId) const { + for (const auto &it : getNodeSet()) { + if (it->getUserId() == nodeUserId) { + return it; + } + } + + return std::nullopt; +} + +template +std::unordered_set>, nodeHash> Graph::nodeSet() { + std::unordered_set>, nodeHash> nodeSet; + for (auto &edgeSetIt : edgeSet) { + nodeSet.insert( + std::const_pointer_cast>(edgeSetIt->getNodePair().first)); + nodeSet.insert( + std::const_pointer_cast>(edgeSetIt->getNodePair().second)); + } + for (auto &isNodeIt : isolatedNodesSet) { + nodeSet.insert(std::const_pointer_cast>(isNodeIt)); + } + + return nodeSet; +} + +template +CXXGraph::id_t Graph::setFind( + std::unordered_map *subsets, + const CXXGraph::id_t nodeId) const { + auto subsets_ptr = + make_shared>(*subsets); + // find root and make root as parent of i + // (path compression) + if ((*subsets)[nodeId].parent != nodeId) { + (*subsets)[nodeId].parent = + Graph::setFind(subsets_ptr, (*subsets)[nodeId].parent); + } + + return (*subsets)[nodeId].parent; +} + +template +CXXGraph::id_t Graph::setFind( + shared> subsets, + const CXXGraph::id_t nodeId) const { + // find root and make root as parent of i + // (path compression) + if ((*subsets)[nodeId].parent != nodeId) { + (*subsets)[nodeId].parent = + Graph::setFind(subsets, (*subsets)[nodeId].parent); + } + + return (*subsets)[nodeId].parent; +} + +template +void Graph::setUnion(std::unordered_map *subsets, + const CXXGraph::id_t elem1, + const CXXGraph::id_t elem2) const { + /* auto subsets_ptr = make_shared>(*subsets); */ + // if both sets have same parent + // then there's nothing to be done + /* if ((*subsets_ptr)[elem1].parent == (*subsets_ptr)[elem2].parent) return; + */ + /* auto elem1Parent = Graph::setFind(subsets_ptr, elem1); */ + /* auto elem2Parent = Graph::setFind(subsets_ptr, elem2); */ + /* if ((*subsets_ptr)[elem1Parent].rank < (*subsets_ptr)[elem2Parent].rank) */ + /* (*subsets_ptr)[elem1].parent = elem2Parent; */ + /* else if ((*subsets_ptr)[elem1Parent].rank > + * (*subsets_ptr)[elem2Parent].rank) */ + /* (*subsets_ptr)[elem2].parent = elem1Parent; */ + /* else { */ + /* (*subsets_ptr)[elem2].parent = elem1Parent; */ + /* (*subsets_ptr)[elem1Parent].rank++; */ + /* } */ + if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; + auto elem1Parent = Graph::setFind(subsets, elem1); + auto elem2Parent = Graph::setFind(subsets, elem2); + if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) + (*subsets)[elem1].parent = elem2Parent; + else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) + (*subsets)[elem2].parent = elem1Parent; + else { + (*subsets)[elem2].parent = elem1Parent; + (*subsets)[elem1Parent].rank++; + } +} + +template +void Graph::setUnion( + shared> subsets, + const CXXGraph::id_t elem1, const CXXGraph::id_t elem2) const { + // if both sets have same parent + // then there's nothing to be done + if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; + auto elem1Parent = Graph::setFind(subsets, elem1); + auto elem2Parent = Graph::setFind(subsets, elem2); + if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) + (*subsets)[elem1].parent = elem2Parent; + else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) + (*subsets)[elem2].parent = elem1Parent; + else { + (*subsets)[elem2].parent = elem1Parent; + (*subsets)[elem1Parent].rank++; + } +} + +template +std::shared_ptr>> Graph::eulerianPath() const { + const auto nodeSet = Graph::getNodeSet(); + + std::shared_ptr>> eulerPath = + std::make_shared>>(); + + bool undirected = this->isUndirectedGraph(); + + std::vector>> currentPath; + // The starting node is the only node which has more outgoing than ingoing + // links + auto firstNodeIt = std::max_element( + nodeSet.begin(), nodeSet.end(), [this](auto n1, auto n2) { + return cachedAdjMatrix->at(n1).size() < cachedAdjMatrix->at(n2).size(); + }); + auto currentNode = *(firstNodeIt); + currentPath.push_back(currentNode); + + while (currentPath.size() > 0) { + auto &edges = cachedAdjMatrix->at(currentNode); + // we keep removing the edges that + // have been traversed from the adjacency list + if (edges.size()) { + auto firstEdge = edges.back().second; + + shared> nextNodeId; + nextNodeId = firstEdge->getOtherNode(currentNode); + + currentPath.push_back(nextNodeId); + currentNode = nextNodeId; + edges.pop_back(); + } else { + eulerPath->push_back(*currentNode); + currentNode = currentPath.back(); + currentPath.pop_back(); + } + } + return eulerPath; +} + +template +shared> Graph::getAdjMatrix() const { + auto adj = std::make_shared>(); + auto addElementToAdjMatrix = [&adj](shared> nodeFrom, + shared> nodeTo, + shared> edge) { + std::pair>, shared>> elem = {nodeTo, + edge}; + (*adj)[nodeFrom].push_back(std::move(elem)); + }; + for (const auto &edgeSetIt : edgeSet) { + if (edgeSetIt->isDirected().has_value() && + edgeSetIt->isDirected().value()) { + shared> d_edge = + std::static_pointer_cast>(edgeSetIt); + addElementToAdjMatrix(d_edge->getNodePair().first, + d_edge->getNodePair().second, d_edge); + } else if (edgeSetIt->isDirected().has_value() && + !edgeSetIt->isDirected().value()) { + shared> ud_edge = + std::static_pointer_cast>(edgeSetIt); + ; + addElementToAdjMatrix(ud_edge->getNodePair().first, + ud_edge->getNodePair().second, ud_edge); + addElementToAdjMatrix(ud_edge->getNodePair().second, + ud_edge->getNodePair().first, ud_edge); + } else { // is a simple edge we cannot create adj matrix + return adj; + } + } + return adj; +} + +template +void Graph::cacheAdjMatrix() { + const auto adj = Graph::getAdjMatrix(); + this->cachedAdjMatrix = adj; +} + +template +shared> Graph::getDegreeMatrix() const { + auto degreeMatrix = std::make_shared>(); + + for (const auto &nodePair : *this->cachedAdjMatrix) { + const shared> &node = nodePair.first; + const std::vector>, shared>>> + &neighbors = nodePair.second; + + int degree = neighbors.size(); + + (*degreeMatrix)[node] = {degree}; + } + + return degreeMatrix; +} + +template +void Graph::cacheDegreeMatrix() { + const auto degreeMatrix = Graph::getDegreeMatrix(); + this->cachedDegreeMatrix = degreeMatrix; +} + +template +shared> Graph::getLaplacianMatrix() const { + const auto adjacencyMatrix = this->cachedAdjMatrix; + const auto degreeMatrix = this->cachedDegreeMatrix; + + auto laplacianMatrix = std::make_shared>(); + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + (*laplacianMatrix)[node] = + std::vector>, shared>>>(); + } + + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + const std::vector>, shared>>> + &neighbors = nodePair.second; + + (*laplacianMatrix)[node].emplace_back(node, + nullptr); // Insere o nó na diagonal + for (const auto &neighborPair : neighbors) { + const shared> &neighbor = neighborPair.first; + (*laplacianMatrix)[node].emplace_back( + neighbor, neighborPair.second); // Insere os pares de vizinhos + } + } + + return laplacianMatrix; +} + +template +void Graph::cacheLaplacianMatrix() { + const auto laplacianMatrix = Graph::getLaplacianMatrix(); + this->cachedLaplacianMatrix = laplacianMatrix; +} + +template +shared> Graph::getTransitionMatrix() const { + const auto adjacencyMatrix = this->cachedAdjMatrix; + + auto transitionMatrix = std::make_shared>(); + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + (*transitionMatrix)[node] = + std::vector>, double>>(); + } + + for (const auto &nodePair : *adjacencyMatrix) { + const shared> &node = nodePair.first; + const std::vector>, shared>>> + &neighbors = nodePair.second; + + int degree = neighbors.size(); + + double transitionProbability = 1.0 / degree; + + for (const auto &neighborPair : neighbors) { + const shared> &neighbor = neighborPair.first; + (*transitionMatrix)[node].emplace_back(neighbor, transitionProbability); + } + } + + return transitionMatrix; +} + +template +void Graph::cacheTransitionMatrix() { + const auto transitionMatrix = Graph::getTransitionMatrix(); + this->cachedTransitionMatrix = transitionMatrix; +} + +template +const std::unordered_set>, nodeHash> +Graph::outNeighbors(const Node *node) const { + auto node_shared = make_shared>(*node); + + return outNeighbors(node_shared); +} + +template +const std::unordered_set>, nodeHash> +Graph::outNeighbors(shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, nodeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, nodeHash> outNeighbors; + for (auto pair : nodeEdgePairs) { + if (pair.second->isDirected().has_value() && + pair.second->isDirected().value()) { + outNeighbors.insert(pair.first); + } + } + + return outNeighbors; +} + +template +const std::unordered_set>, nodeHash> +Graph::inOutNeighbors(const Node *node) const { + auto node_shared = make_shared>(*node); + + return inOutNeighbors(node_shared); +} + +template +const std::unordered_set>, nodeHash> +Graph::inOutNeighbors(shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, nodeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, nodeHash> inOutNeighbors; + for (auto pair : nodeEdgePairs) { + inOutNeighbors.insert(pair.first); + } + + return inOutNeighbors; +} + +template +const std::unordered_set>, edgeHash> Graph::outEdges( + const Node *node) const { + auto node_shared = make_shared>(*node); + + return outEdges(node_shared); +} + +template +const std::unordered_set>, edgeHash> Graph::outEdges( + shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, edgeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, edgeHash> outEdges; + for (auto pair : nodeEdgePairs) { + if (pair.second->isDirected().has_value() && + pair.second->isDirected().value()) { + outEdges.insert(pair.second); + } + } + + return outEdges; +} + +template +const std::unordered_set>, edgeHash> +Graph::inOutEdges(const Node *node) const { + auto node_shared = make_shared>(*node); + + return outEdges(node_shared); +} + +template +const std::unordered_set>, edgeHash> +Graph::inOutEdges(shared> node) const { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { + return std::unordered_set>, edgeHash>(); + } + auto nodeEdgePairs = cachedAdjMatrix->at(node); + + std::unordered_set>, edgeHash> outEdges; + for (auto pair : nodeEdgePairs) { + outEdges.insert(pair.second); + } + + return outEdges; +} + +template +bool Graph::isDirectedGraph() const { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if (!(edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Undirected Edge + return false; + } + } + // No Undirected Edge + return true; +} + +template +bool Graph::isUndirectedGraph() const { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if ((edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Directed Edge + return false; + } + } + // No Directed Edge + return true; +} + +template +void Graph::reverseDirectedGraph() { + if (!isDirectedGraph()) { + throw std::runtime_error(ERR_UNDIR_GRAPH); + } + auto oldEdgeSet = Graph::getEdgeSet(); + for (const auto &edge : oldEdgeSet) { + auto &[first, second] = edge->getNodePair(); + auto id = edge->getId(); + this->removeEdge(id); + auto newEdge = std::make_shared>(id, second, first); + this->addEdge(newEdge); + } +} + +template +const std::vector> Graph::graph_slicing(const Node &start) const { + std::vector> result; + + auto nodeSet = Graph::getNodeSet(); + // check if start node in the graph + auto start_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&start](auto node) { return node->getUserId() == start.getUserId(); }); + if (start_node_it == nodeSet.end()) { + return result; + } + std::vector> C = Graph::depth_first_search(start); + std::deque>> C1; // complement of C i.e. nodeSet - C + for (auto const &node : nodeSet) { + // from the set of all nodes, remove nodes that exist in C + if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { + return (*node == nodeC); + }) == C.end()) + C1.push_back(node); + } + + // For all nodes in C', apply DFS + // and get the list of reachable nodes and store in M + std::vector> M; + for (auto const &node : C1) { + std::vector> reachableNodes = Graph::depth_first_search(*node); + M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); + } + // removes nodes from C that are reachable from M. + for (const auto &nodeC : C) { + if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { + return (nodeM == nodeC); + }) == M.end()) + result.push_back(nodeC); + } + return result; +} + + +template +std::ostream &operator<<(std::ostream &os, const Graph &graph) { + os << "Graph:\n"; + auto edgeList = graph.getEdgeSet(); + for (auto it = edgeList.begin(); it != edgeList.end(); ++it) { + if (!(*it)->isDirected().has_value() && !(*it)->isWeighted().has_value()) { + // Edge Case + os << **it << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + ((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) + << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + !((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) << "\n"; + } else if (!((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + ((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) + << "\n"; + } else if (!((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + !((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << *std::static_pointer_cast>(*it) << "\n"; + } else { + os << *it << "\n"; + } + } + return os; +} + +template +std::ostream &operator<<(std::ostream &os, const AdjacencyMatrix &adj) { + os << "Adjacency Matrix:\n"; + unsigned long max_column = 0; + for (const auto &it : adj) { + if (it.second.size() > max_column) { + max_column = (unsigned long)it.second.size(); + } + } + if (max_column == 0) { + os << "ERROR in Print\n"; + return os; + } else { + os << "|--|"; + for (unsigned long i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + for (const auto &it : adj) { + os << "|N" << it.first->getId() << "|"; + for (const auto &it2 : it.second) { + os << "N" << it2.first->getId() << ",E" << it2.second->getId() << "|"; + } + os << "\n|--|"; + for (unsigned long i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + } + } + return os; +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_GRAPH_IMPL_H__ diff --git a/include/CXXGraph/Graph/IO/IOUtility_impl.hpp b/include/CXXGraph/Graph/IO/IOUtility_impl.hpp new file mode 100644 index 000000000..767ba1edd --- /dev/null +++ b/include/CXXGraph/Graph/IO/IOUtility_impl.hpp @@ -0,0 +1,121 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_IOUTILITY_IMPL_H__ +#define __CXXGRAPH_IOUTILITY_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { +template +std::optional> Graph::getExtenstionAndSeparator( + InputOutputFormat format) const { + if (format == InputOutputFormat::STANDARD_CSV) { + return std::pair(".csv", ','); + } else if (format == InputOutputFormat::STANDARD_TSV) { + return std::pair(".tsv", '\t'); + } else { + return std::nullopt; + } +} + + + +// This ctype facet classifies ',' and '\t' as whitespace +struct csv_whitespace : std::ctype { + static const mask *make_table() { + // make a copy of the "C" locale table + static std::vector v(classic_table(), classic_table() + table_size); + v[','] |= space; // comma will be classified as whitespace + v['\t'] |= space; + v[' '] &= ~space; // space will not be classified as whitespace + return &v[0]; + } + explicit csv_whitespace(std::size_t refs = 0) : ctype(make_table(), false, refs) {} +}; + +#ifdef WITH_COMPRESSION +template +int Graph::compressFile(const std::string &inputFile, + const std::string &outputFile) const { + std::ifstream ifs; + ifs.open(inputFile); + if (!ifs.is_open()) { + // ERROR File Not Open + return -1; + } + std::string content((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + + const char *content_ptr = content.c_str(); + gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); + if (outFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); + return -1; + } + + unsigned int zippedBytes; + zippedBytes = + gzwrite(outFileZ, content_ptr, (unsigned int)strlen(content_ptr)); + + ifs.close(); + gzclose(outFileZ); + return zippedBytes; +} + +template +int Graph::decompressFile(const std::string &inputFile, + const std::string &outputFile) const { + gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); + if (inFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); + return -1; + } + unsigned char unzipBuffer[8192]; + std::vector unzippedData; + std::ofstream ofs; + ofs.open(outputFile); + if (!ofs.is_open()) { + // ERROR File Not Open + return -1; + } + while (true) { + unsigned int unzippedBytes; + unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); + if (unzippedBytes > 0) { + unzippedData.insert(unzippedData.end(), unzipBuffer, + unzipBuffer + unzippedBytes); + } else { + break; + } + } + for (const auto &c : unzippedData) { + ofs << c; + } + ofs << std::endl; + ofs.close(); + gzclose(inFileZ); + return 0; +} +#endif + +} // namespace CXXGraph +#endif // __CXXGRAPH_IOUTILITY_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/IO/InputOperation_impl.hpp b/include/CXXGraph/Graph/IO/InputOperation_impl.hpp new file mode 100644 index 000000000..8c52f24c7 --- /dev/null +++ b/include/CXXGraph/Graph/IO/InputOperation_impl.hpp @@ -0,0 +1,439 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_INPUTOPERATION_IMPL_H__ +#define __CXXGRAPH_INPUTOPERATION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +int Graph::readFromFile(InputOutputFormat format, + const std::string &workingDir, + const std::string &OFileName, bool compress, + bool readNodeFeat, bool readEdgeWeight) { + int result = 0; + +#ifdef WITH_COMPRESSION + if (compress) { + auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, + &readEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = decompressFile(completePathToFileGraphCompressed, + completePathToFileGraph); + if (_result == 0) { + if (readNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; + _result = decompressFile(completePathToFileNodeFeatCompressed, + completePathToFileNodeFeat); + } + } + if (_result == 0) { + if (readEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; + _result = decompressFile(completePathToFileEdgeWeightCompressed, + completePathToFileEdgeWeight); + } + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + result = decompress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + result = decompress(".tsv"); + } else { + // INPUT FORMAT NOT RECOGNIZED + result = -1; + } + + if (result != 0) { + return result; + } + } +#endif + // Open streams and read + auto extSep = getExtenstionAndSeparator(format); + if (!extSep) { + std::cerr << "Unknown format\n"; + return -1; + } + auto &[extension, separator] = *extSep; + + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileNodeFeat; + std::string completePathToFileEdgeWeight; + + std::ifstream ifileGraph; + std::ifstream ifileNodeFeat; + std::ifstream ifileEdgeWeight; + + ifileGraph.open(completePathToFileGraph); + if (!ifileGraph.is_open()) { + // ERROR File Not Open + // std::cout << "ERROR File Not Open : " << completePathToFileGraph << + // std::endl; + return -1; + } + ifileGraph.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); + + if (readNodeFeat) { + completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + ifileNodeFeat.open(completePathToFileNodeFeat); + if (!ifileNodeFeat.is_open()) { + // ERROR File Not Open + // std::cout << "ERROR File Not Open" << std::endl; + return -1; + } + ifileNodeFeat.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); + } + + if (readEdgeWeight) { + completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + ifileEdgeWeight.open(completePathToFileEdgeWeight); + if (!ifileEdgeWeight.is_open()) { + // ERROR File Not Open + // std::cout << "ERROR File Not Open" << std::endl; + return -1; + } + ifileEdgeWeight.imbue(std::locale(ifileGraph.getloc(), new csv_whitespace)); + } + + readGraphFromStream(ifileGraph, ifileNodeFeat, ifileEdgeWeight, readNodeFeat, + readEdgeWeight); + + // Cleanup + ifileGraph.close(); +#ifdef WITH_COMPRESSION + if (compress) remove(completePathToFileGraph.c_str()); +#endif + + if (readNodeFeat) { + ifileNodeFeat.close(); +#ifdef WITH_COMPRESSION + if (compress) remove(completePathToFileNodeFeat.c_str()); +#endif + } + + if (readEdgeWeight) { + ifileEdgeWeight.close(); +#ifdef WITH_COMPRESSION + if (compress) remove(completePathToFileEdgeWeight.c_str()); +#endif + } + + return result; +} + +template +int Graph::readFromDotFile(const std::string &workingDir, + const std::string &fileName) { + return readFromDot(workingDir, fileName); +} + +template +int Graph::readFromMTXFile(const std::string &workingDir, + const std::string &fileName) { + // Define the edge maps + std::unordered_map> + edgeMap; + std::unordered_map nodeFeatMap; + std::unordered_map edgeDirectedMap; + std::unordered_map edgeWeightMap; + + // Get full path to the file and open it + const std::string completePathToFileGraph = + workingDir + '/' + fileName + ".mtx"; + std::ifstream iFile(completePathToFileGraph); + // Check that the file is open + if (!iFile.is_open()) { + return -1; + } + + // Define the number of columns and rows in the matrix + int n_cols, n_rows; + int n_edges; + bool undirected = false; + + // From the first line of the file read the number of rows, columns and edges + std::string row_content; + getline(iFile, row_content); + if (row_content.find("symmetric") != std::string::npos) { + undirected = true; + } + + // Get rid of any commented lines between the header and the size line + while (row_content.find('%') != std::string::npos) { + getline(iFile, row_content); + } + + // From the size line of the file read the number of rows, columns and edges + std::stringstream header_stream(row_content); + std::string value; + getline(header_stream, value, ' '); + n_rows = std::stoi(value); + getline(header_stream, value, ' '); + n_cols = std::stoi(value); + getline(header_stream, value, ' '); + n_edges = std::stoi(value); + + // Since the matrix represents the adjacency matrix, it must be square + if (n_rows != n_cols) { + return -1; + } + + // Read the content of each line + std::string node1; + std::string node2; + std::string edge_weight; + CXXGraph::id_t edge_id = 0; + while (getline(iFile, row_content)) { + std::stringstream row_stream(row_content); + + // Read the content of the node ids and the weight into strings + getline(row_stream, node1, ' '); + getline(row_stream, node2, ' '); + getline(row_stream, edge_weight); + + edgeMap[edge_id] = std::pair(node1, node2); + edgeWeightMap[edge_id] = std::stod(edge_weight); + edgeDirectedMap[edge_id] = !undirected; + + // If the edge is a self-link, it must be undirected + if (node1 == node2) { + edgeDirectedMap[edge_id] = false; + } + + // Increase the edge id + ++edge_id; + } + + if (n_edges != edgeMap.size()) { + std::cout << "Error: The number of edges does not match the value provided " + "in the size line.\n"; + return -1; + } + + iFile.close(); + recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); + return 0; +} + +template +int Graph::readFromDot(const std::string &workingDir, + const std::string &fileName) { + // Define the edge maps + std::unordered_map> + edgeMap; + std::unordered_map nodeFeatMap; + std::unordered_map edgeDirectedMap; + std::unordered_map edgeWeightMap; + + // Define the node strings and the "garbage collector" temp string + std::string node1; + std::string node2; + std::string temp; + + // Get full path to the file and open it + const std::string completePathToFileGraph = + workingDir + '/' + fileName + ".dot"; + + // Check if the graph is directed + bool directed = false; + std::ifstream fileContentStream(completePathToFileGraph); + std::string fileContent(std::istreambuf_iterator{fileContentStream}, + {}); + if (fileContent.find("->") != std::string::npos) { + directed = true; + } + // Check if the graph is weighted + bool weighted = false; + if (fileContent.find("weight") != std::string::npos) { + weighted = true; + } + fileContentStream.close(); + + std::ifstream iFile(completePathToFileGraph); + // Write the header of the DOT file in the temp string + getline(iFile, temp); + + CXXGraph::id_t edgeId = 0; + std::string fileRow; + while (getline(iFile, fileRow)) { + // If you've reached the end of the file, stop + if (fileRow == "}") { + break; + } + + // Remove the whitespaces before the definition of the edge + while (*fileRow.begin() == ' ' || *fileRow.begin() == '\t') { + fileRow.erase(fileRow.begin()); + } + + std::stringstream row_stream(fileRow); + getline(row_stream, node1, ' '); + // Store the symbol representing the edge inside temp + getline(row_stream, temp, ' '); + if (weighted) { + getline(row_stream, node2, '['); + // Remove any whitespaces or tabs from the node string + node2.erase(std::remove(node2.begin(), node2.end(), ' '), node2.end()); + node2.erase(std::remove(node2.begin(), node2.end(), '\t'), node2.end()); + + getline(row_stream, temp, '='); + std::string weight; + getline(row_stream, weight, ']'); + // Erase any whitespaces + weight.erase(std::remove(weight.begin(), weight.end(), ' '), + weight.end()); + edgeWeightMap[edgeId] = std::stod(weight); + } else { + getline(row_stream, node2, ';'); + } + + // Save the edge and increment the edge counter + edgeMap[edgeId] = std::pair(node1, node2); + edgeDirectedMap[edgeId] = directed; + ++edgeId; + } + iFile.close(); + + recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); + return 0; +} + +template +void Graph::readGraphFromStream(std::istream &iGraph, + std::istream &iNodeFeat, + std::istream &iEdgeWeight, bool readNodeFeat, + bool readEdgeWeight) { + std::unordered_map> + edgeMap; + std::unordered_map edgeDirectedMap; + std::unordered_map nodeFeatMap; + std::unordered_map edgeWeightMap; + + CXXGraph::id_t edgeId; + std::string nodeId1; + std::string nodeId2; + bool directed; + while (iGraph >> edgeId >> nodeId1 >> nodeId2 >> + directed) { /* loop continually */ + edgeMap[edgeId] = std::pair(nodeId1, nodeId2); + edgeDirectedMap[edgeId] = directed; + } + + if (readNodeFeat) { + std::string nodeId; + T nodeFeat; + while (iNodeFeat >> nodeId >> nodeFeat) { + nodeFeatMap[nodeId] = nodeFeat; + } + } + + if (readEdgeWeight) { + CXXGraph::id_t edgeId; + double weight; + bool weighted; + while (iEdgeWeight >> edgeId >> weight >> weighted) { /* loop continually */ + if (weighted) { + edgeWeightMap[edgeId] = weight; + } + } + } + + recreateGraph(edgeMap, edgeDirectedMap, nodeFeatMap, edgeWeightMap); +} + + + +template +void Graph::recreateGraph( + std::unordered_map> + &edgeMap, + std::unordered_map &edgeDirectedMap, + std::unordered_map &nodeFeatMap, + std::unordered_map &edgeWeightMap) { + std::unordered_map>> nodeMap; + for (const auto &edgeIt : edgeMap) { + shared> node1(nullptr); + shared> node2(nullptr); + if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { + feat = std::move(nodeFeatMap.at(edgeIt.second.first)); + } + node1 = make_shared>(edgeIt.second.first, feat); + nodeMap[edgeIt.second.first] = node1; + } else { + node1 = nodeMap.at(edgeIt.second.first); + } + if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { + feat = std::move(nodeFeatMap.at(edgeIt.second.second)); + } + node2 = make_shared>(edgeIt.second.second, feat); + nodeMap[edgeIt.second.second] = node2; + } else { + node2 = nodeMap.at(edgeIt.second.second); + } + + if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = make_shared>( + edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } else { + auto edge = make_shared>( + edgeIt.first, node1, node2, edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } + } else { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = make_shared>(edgeIt.first, node1, node2); + addEdge(edge); + } else { + auto edge = make_shared>(edgeIt.first, node1, node2); + addEdge(edge); + } + } + } +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_INPUTOPERATION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp b/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp new file mode 100644 index 000000000..99d5ef95e --- /dev/null +++ b/include/CXXGraph/Graph/IO/OutputOperation_impl.hpp @@ -0,0 +1,289 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_OUTPUTOPERATION_IMPL_H__ +#define __CXXGRAPH_OUTPUTOPERATION_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + + +template +int Graph::writeToFile(InputOutputFormat format, + const std::string &workingDir, + const std::string &OFileName, bool compress, + bool writeNodeFeat, bool writeEdgeWeight) const { + int result = 0; + + // Open streams and write + auto extSep = getExtenstionAndSeparator(format); + if (!extSep) { + std::cerr << "Unknown format\n"; + return -1; + } + auto &[extension, separator] = *extSep; + + std::ofstream ofileGraph; + std::ofstream ofileNodeFeat; + std::ofstream ofileEdgeWeight; + + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + ofileGraph.open(completePathToFileGraph); + if (!ofileGraph.is_open()) { + // ERROR File Not Open + return -1; + } + + if (writeNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + ofileNodeFeat.open(completePathToFileNodeFeat); + if (!ofileNodeFeat.is_open()) { + // ERROR File Not Open + return -1; + } + } + + if (writeEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + ofileEdgeWeight.open(completePathToFileEdgeWeight); + if (!ofileEdgeWeight.is_open()) { + // ERROR File Not Open + std::cout << "ERROR File Not Open" << std::endl; + return -1; + } + } + + writeGraphToStream(ofileGraph, ofileNodeFeat, ofileEdgeWeight, separator, + writeNodeFeat, writeEdgeWeight); + + // Cleanup from writing + ofileGraph.close(); + if (writeNodeFeat) ofileNodeFeat.close(); + if (writeEdgeWeight) ofileEdgeWeight.close(); + +#ifdef WITH_COMPRESSION + if (result == 0 && compress) { + auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, + &writeEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = compressFile(completePathToFileGraph, + completePathToFileGraphCompressed); + if (_result > 0) { + _result = remove(completePathToFileGraph.c_str()); + } + if (_result == 0) { + if (writeNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; + _result = compressFile(completePathToFileNodeFeat, + completePathToFileNodeFeatCompressed); + if (_result > 0) { + _result = remove(completePathToFileNodeFeat.c_str()); + } + } + } + if (_result == 0) { + if (writeEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; + _result = compressFile(completePathToFileEdgeWeight, + completePathToFileEdgeWeightCompressed); + if (_result > 0) { + _result = remove(completePathToFileEdgeWeight.c_str()); + } + } + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + auto result = _compress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + auto result = _compress(".tsv"); + } else { + // OUTPUT FORMAT NOT RECOGNIZED + result = -1; + } + } +#endif + + return result; +} + +template +int Graph::writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const { + return writeToDot(workingDir, OFileName, graphName); +} + +template +int Graph::writeToMTXFile(const std::string &workingDir, + const std::string &OFileName, + char delimitier) const { + // Get the full path and open the file + const std::string completePathToFileGraph = + workingDir + '/' + OFileName + ".mtx"; + std::ofstream iFile(completePathToFileGraph); + + // Write the header of the file + std::string header = "%%MatrixMarket graph"; + // Check if the adjacency matrix is symmetric, i.e., if all the edges are + // undirected + bool symmetric = !std::any_of(edgeSet.begin(), edgeSet.end(), [](auto edge) { + return (edge->isDirected().has_value() && edge->isDirected().value()); + }); + // Write in the header whether the adj matrix is symmetric or not + if (symmetric) { + header += " symmetric\n"; + } else { + header += '\n'; + } + iFile << header; + + // Write the line containing the number of nodes and edges + const std::string firstLine = + std::to_string(getNodeSet().size()) + delimitier + + std::to_string(getNodeSet().size()) + delimitier + + std::to_string(getEdgeSet().size()) + '\n'; + iFile << firstLine; + + // Construct the edges + for (const auto &edgeIt : edgeSet) { + std::string line; + line += edgeIt->getNodePair().first->getUserId() + delimitier; + line += edgeIt->getNodePair().second->getUserId() + delimitier; + if (edgeIt->isWeighted().has_value() && edgeIt->isWeighted().value()) { + line += std::to_string(edgeIt->isWeighted().value()) + '\n'; + } else { + line += std::to_string(1.) + '\n'; + } + iFile << line; + } + + iFile.close(); + return 0; +} + +template +int Graph::writeToDot(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const { + const std::string linkSymbol = "--"; + const std::string directedLinkSymbol = "->"; + + const std::string completePathToFileGraph = + workingDir + '/' + OFileName + ".dot"; + std::ofstream ofileGraph; + ofileGraph.open(completePathToFileGraph); + if (!ofileGraph.is_open()) { + // ERROR File Not Open + return -1; + } + + // Write the header of the DOT file + std::string headerLine; + if (this->isDirectedGraph()) { + headerLine = "digraph " + graphName + " {\n"; + } else { + headerLine = "graph " + graphName + " {\n"; + } + ofileGraph << headerLine; + + for (auto const &edgePtr : edgeSet) { + std::string edgeLine = ""; + if (edgePtr->isDirected().has_value() && edgePtr->isDirected().value()) { + auto directedPtr = + std::static_pointer_cast>(edgePtr); + edgeLine += '\t' + directedPtr->getFrom().getUserId() + ' '; + edgeLine += directedLinkSymbol + ' '; + edgeLine += directedPtr->getTo().getUserId(); + } else { + edgeLine += '\t' + edgePtr->getNodePair().first->getUserId() + ' '; + edgeLine += linkSymbol + ' '; + edgeLine += edgePtr->getNodePair().second->getUserId(); + } + if (edgePtr->isWeighted().has_value() && edgePtr->isWeighted().value()) { + // Weights in dot files must be integers + edgeLine += " [weight=" + + std::to_string(static_cast( + std::dynamic_pointer_cast(edgePtr) + ->getWeight())) + + ']'; + } + edgeLine += ";\n"; + ofileGraph << edgeLine; + } + ofileGraph << '}'; + ofileGraph.close(); + + return 0; +} + +template +void Graph::writeGraphToStream(std::ostream &oGraph, std::ostream &oNodeFeat, + std::ostream &oEdgeWeight, const char &sep, + bool writeNodeFeat, + bool writeEdgeWeight) const { + for (const auto &edge : edgeSet) { + oGraph << edge->getId() << sep << edge->getNodePair().first->getUserId() + << sep << edge->getNodePair().second->getUserId() << sep + << ((edge->isDirected().has_value() && edge->isDirected().value()) + ? 1 + : 0) + << std::endl; + } + + if (writeNodeFeat) { + auto nodeSet = getNodeSet(); + for (const auto &node : nodeSet) { + oNodeFeat << node->getUserId() << sep << node->getData() << std::endl; + } + } + + if (writeEdgeWeight) { + for (const auto &edge : edgeSet) { + oEdgeWeight + << edge->getId() << sep + << (edge->isWeighted().has_value() && edge->isWeighted().value() + ? (std::dynamic_pointer_cast(edge)) + ->getWeight() + : 0.0) + << sep + << (edge->isWeighted().has_value() && edge->isWeighted().value() ? 1 + : 0) + << std::endl; + } + } +} + +} // namespace CXXGraph +#endif // __CXXGRAPH_OUTPUTOPERATION_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Node/Node.h b/include/CXXGraph/Node/Node.h new file mode 100755 index 000000000..ec61b6856 --- /dev/null +++ b/include/CXXGraph/Node/Node.h @@ -0,0 +1,27 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_NODE_H__ +#define __CXXGRAPH_NODE_H__ + +#pragma once + +#include "Node_impl.hpp" + +#endif // __CXXGRAPH_NODE_H__ diff --git a/include/CXXGraph/Node/Node_decl.h b/include/CXXGraph/Node/Node_decl.h new file mode 100644 index 000000000..c47124a10 --- /dev/null +++ b/include/CXXGraph/Node/Node_decl.h @@ -0,0 +1,58 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_NODE_DECL_H__ +#define __CXXGRAPH_NODE_DECL_H__ + +#pragma once +#include + +#include "CXXGraph/Utility/id_t.hpp" + +namespace CXXGraph { +template +class Node; +template +std::ostream &operator<<(std::ostream &os, const Node &node); +template +class Node { + private: + CXXGraph::id_t id = 0; + std::string userId = ""; + T data; + void setId(const std::string &); + + public: + Node(const std::string &, const T &data); + // Move constructor + Node(const std::string &, T &&data) noexcept; + ~Node() = default; + const CXXGraph::id_t &getId() const; + const std::string &getUserId() const; + const T &getData() const; + void setData(T &&new_data); + // operator + bool operator==(const Node &b) const; + bool operator<(const Node &b) const; + friend std::ostream &operator<< <>(std::ostream &os, const Node &node); +}; + +} // namespace CXXGraph + +#endif // __CXXGRAPH_NODE_DECL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Node/Node.hpp b/include/CXXGraph/Node/Node_impl.hpp old mode 100755 new mode 100644 similarity index 58% rename from include/CXXGraph/Node/Node.hpp rename to include/CXXGraph/Node/Node_impl.hpp index b191344e5..3b25ba0f5 --- a/include/CXXGraph/Node/Node.hpp +++ b/include/CXXGraph/Node/Node_impl.hpp @@ -17,46 +17,19 @@ /*** License: AGPL v3.0 ***/ /***********************************************************/ -#ifndef __CXXGRAPH_NODE_H__ -#define __CXXGRAPH_NODE_H__ - -#pragma once - -#include "CXXGraph/Utility/id_t.hpp" +#ifndef __CXXGRAPH_NODE_IMPL_H__ +#define __CXXGRAPH_NODE_IMPL_H__ +#include "Node_decl.h" #include -#include namespace CXXGraph { + template class Node; -template -std::ostream &operator<<(std::ostream &os, const Node &node); -template -class Node { - private: - CXXGraph::id_t id = 0; - std::string userId = ""; - T data; - void setId(const std::string &); - - public: - Node(const std::string &, const T& data); - // Move constructor - Node(const std::string &, T&& data) noexcept; - ~Node() = default; - const CXXGraph::id_t &getId() const; - const std::string &getUserId() const; - const T &getData() const; - void setData(T&& new_data); - // operator - bool operator==(const Node &b) const; - bool operator<(const Node &b) const; - friend std::ostream &operator<< <>(std::ostream &os, const Node &node); -}; template -Node::Node(const std::string& id, const T& data) { +Node::Node(const std::string &id, const T &data) { this->userId = id; // the userid is set as sha512 hash of the user provided id setId(id); @@ -64,7 +37,7 @@ Node::Node(const std::string& id, const T& data) { } template -Node::Node(const std::string& id, T&& data) noexcept { +Node::Node(const std::string &id, T &&data) noexcept { this->userId = id; // the userid is set as sha512 hash of the user provided id setId(id); @@ -73,27 +46,6 @@ Node::Node(const std::string& id, T&& data) noexcept { template void Node::setId(const std::string &inpId) { - // const unsigned char* userId = reinterpret_cast((*inpId).c_str() ); unsigned char obuf[64]; unsigned long long obuf[8]; - // SHA512(userId, (*inpId).length(), reinterpret_cast(obuf)); - /** - // Transform byte-array to string - std::stringstream shastr; - shastr << std::hex << std::setfill('0'); - int i = 0; - //unsigned long can only store 8 bytes so we truncate the hash to 8 bytes - for (const auto &byte: obuf) - { - shastr << std::setw(2) << static_cast(byte); - i++; - if (i==8) break; - } - auto idStr = shastr.str(); - // convert hex string to unsigned long long - std::istringstream iss(idStr); - iss >> std::hex >> this->id; - - **/ this->id = std::hash{}(inpId); } @@ -113,7 +65,7 @@ const T &Node::getData() const { } template -void Node::setData(T&& new_data) { +void Node::setData(T &&new_data) { this->data = std::move(new_data); } @@ -136,6 +88,7 @@ std::ostream &operator<<(std::ostream &os, const Node &node) { << " Id:\t" << node.userId << "\n Data:\t" << node.data << "\n}"; return os; } + } // namespace CXXGraph -#endif // __CXXGRAPH_NODE_H__ +#endif // __CXXGRAPH_NODE_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp b/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp index 22ab4052c..3005cd778 100755 --- a/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp +++ b/include/CXXGraph/Partitioning/CoordinatedPartitionState.hpp @@ -28,7 +28,7 @@ #include #include "CoordinatedRecord.hpp" -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionState.hpp" #include "CXXGraph/Partitioning/Utility/Globals.hpp" #include "Record.hpp" diff --git a/include/CXXGraph/Partitioning/EBV.hpp b/include/CXXGraph/Partitioning/EBV.hpp index f9cece655..4a1d3a0b9 100755 --- a/include/CXXGraph/Partitioning/EBV.hpp +++ b/include/CXXGraph/Partitioning/EBV.hpp @@ -26,7 +26,7 @@ #include #include -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionStrategy.hpp" #include "CXXGraph/Partitioning/Utility/Globals.hpp" diff --git a/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp b/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp index 21c326d0c..9baeed4aa 100755 --- a/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp +++ b/include/CXXGraph/Partitioning/EdgeBalancedVertexCut.hpp @@ -25,7 +25,7 @@ #include -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionStrategy.hpp" #include "CXXGraph/Partitioning/Utility/Globals.hpp" diff --git a/include/CXXGraph/Partitioning/GreedyVertexCut.hpp b/include/CXXGraph/Partitioning/GreedyVertexCut.hpp index ddc7ff538..7fd181205 100755 --- a/include/CXXGraph/Partitioning/GreedyVertexCut.hpp +++ b/include/CXXGraph/Partitioning/GreedyVertexCut.hpp @@ -25,8 +25,9 @@ #include #include +#include -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionStrategy.hpp" #include "CXXGraph/Partitioning/Utility/Globals.hpp" diff --git a/include/CXXGraph/Partitioning/HDRF.hpp b/include/CXXGraph/Partitioning/HDRF.hpp index cc8ed3430..bf968f953 100755 --- a/include/CXXGraph/Partitioning/HDRF.hpp +++ b/include/CXXGraph/Partitioning/HDRF.hpp @@ -26,7 +26,7 @@ #include #include -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionStrategy.hpp" #include "CXXGraph/Partitioning/Utility/Globals.hpp" diff --git a/include/CXXGraph/Partitioning/PartitionStrategy.hpp b/include/CXXGraph/Partitioning/PartitionStrategy.hpp index 2110bdbef..a32f7e1a0 100755 --- a/include/CXXGraph/Partitioning/PartitionStrategy.hpp +++ b/include/CXXGraph/Partitioning/PartitionStrategy.hpp @@ -22,7 +22,7 @@ #pragma once -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionState.hpp" namespace CXXGraph { diff --git a/include/CXXGraph/Partitioning/Partitioner.hpp b/include/CXXGraph/Partitioning/Partitioner.hpp index b629b8561..ece81055c 100755 --- a/include/CXXGraph/Partitioning/Partitioner.hpp +++ b/include/CXXGraph/Partitioning/Partitioner.hpp @@ -26,7 +26,7 @@ #include "CoordinatedPartitionState.hpp" #include "EBV.hpp" -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "EdgeBalancedVertexCut.hpp" #include "GreedyVertexCut.hpp" #include "HDRF.hpp" @@ -35,6 +35,7 @@ #include "PartitionerThread.hpp" #include "CXXGraph/Partitioning/Utility/Globals.hpp" #include "CXXGraph/Utility/Runnable.hpp" +#include "CXXGraph/Graph/Graph.h" #include "WeightBalancedLibra.hpp" namespace CXXGraph { @@ -57,11 +58,30 @@ class Partitioner { CoordinatedPartitionState startCoordinated(); - public: Partitioner(shared> dataset, Globals &G); Partitioner(const Partitioner &other); CoordinatedPartitionState performCoordinatedPartition(); + + public: + + /** + * \brief + * This function partition a graph in a set of partitions + * Note: No Thread Safe + * + * @param graph The Graph to partition + * @param algorithm The partition algorithm + * @param numberOfPartition The number of partitions + * @return The partiton Map of the partitioned graph + */ + static PartitionMap partitionGraph( + const Graph& graph, + const Partitioning::PartitionAlgorithm algorithm, + const unsigned int numberOfPartitions, const double param1 = 0.0, + const double param2 = 0.0, const double param3 = 0.0, + const unsigned int numberOfthreads = + std::thread::hardware_concurrency()); }; template Partitioner::Partitioner(shared> dataset, Globals &G) @@ -197,6 +217,27 @@ CoordinatedPartitionState Partitioner::performCoordinatedPartition() { return startCoordinated(); } +template +PartitionMap Partitioner::partitionGraph( + const Graph& graph, + const Partitioning::PartitionAlgorithm algorithm, + const unsigned int numberOfPartitions, const double param1, + const double param2, const double param3, + const unsigned int numberOfThreads){ + PartitionMap partitionMap; + Partitioning::Globals globals(numberOfPartitions, algorithm, param1, param2, + param3, numberOfThreads); + auto edgeSet_ptr = make_shared>(graph.getEdgeSet()); + globals.edgeCardinality = edgeSet_ptr->size(); + globals.vertexCardinality = graph.getNodeSet().size(); + Partitioning::Partitioner partitioner(edgeSet_ptr, globals); + Partitioning::CoordinatedPartitionState partitionState = + partitioner.performCoordinatedPartition(); + partitionMap = partitionState.getPartitionMap(); + return partitionMap; +} + + } // namespace Partitioning } // namespace CXXGraph diff --git a/include/CXXGraph/Partitioning/PartitionerThread.hpp b/include/CXXGraph/Partitioning/PartitionerThread.hpp index a33913eed..32193b65a 100755 --- a/include/CXXGraph/Partitioning/PartitionerThread.hpp +++ b/include/CXXGraph/Partitioning/PartitionerThread.hpp @@ -25,7 +25,7 @@ #include #include -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionState.hpp" #include "PartitionStrategy.hpp" #include "CXXGraph/Utility/Runnable.hpp" diff --git a/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp b/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp index eaf38e0dd..117cc66e9 100755 --- a/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp +++ b/include/CXXGraph/Partitioning/WeightBalancedLibra.hpp @@ -24,7 +24,7 @@ #include -#include "CXXGraph/Edge/Edge.hpp" +#include "CXXGraph/Edge/Edge.h" #include "PartitionStrategy.hpp" #include "CXXGraph/Partitioning/Utility/Globals.hpp" diff --git a/include/CXXGraph/Utility/PointerHash.hpp b/include/CXXGraph/Utility/PointerHash.hpp index 399d6dd7a..5027255ec 100755 --- a/include/CXXGraph/Utility/PointerHash.hpp +++ b/include/CXXGraph/Utility/PointerHash.hpp @@ -25,13 +25,13 @@ #include #include -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Node/Node.hpp" +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Node/Node.h" namespace CXXGraph { template diff --git a/include/CXXGraph/Utility/Reader.hpp b/include/CXXGraph/Utility/Reader.hpp index 0059e1c53..4c8b64c69 100755 --- a/include/CXXGraph/Utility/Reader.hpp +++ b/include/CXXGraph/Utility/Reader.hpp @@ -23,6 +23,9 @@ #pragma once // This is to make sure that this header is only included once namespace CXXGraph { +//Foward declaration +template +class Graph; /*! Interface to implement for a custom reader. */ diff --git a/include/CXXGraph/Utility/TypeTraits.hpp b/include/CXXGraph/Utility/TypeTraits.hpp index 3c47c36b5..7806fba45 100755 --- a/include/CXXGraph/Utility/TypeTraits.hpp +++ b/include/CXXGraph/Utility/TypeTraits.hpp @@ -24,13 +24,13 @@ #include -#include "CXXGraph/Edge/DirectedEdge.hpp" -#include "CXXGraph/Edge/DirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Edge.hpp" -#include "CXXGraph/Edge/UndirectedEdge.hpp" -#include "CXXGraph/Edge/UndirectedWeightedEdge.hpp" -#include "CXXGraph/Edge/Weighted.hpp" -#include "CXXGraph/Node/Node.hpp" +#include "CXXGraph/Edge/DirectedEdge.h" +#include "CXXGraph/Edge/DirectedWeightedEdge.h" +#include "CXXGraph/Edge/Edge.h" +#include "CXXGraph/Edge/UndirectedEdge.h" +#include "CXXGraph/Edge/UndirectedWeightedEdge.h" +#include "CXXGraph/Edge/Weighted.h" +#include "CXXGraph/Node/Node.h" namespace CXXGraph { diff --git a/include/CXXGraph/Utility/Typedef.hpp b/include/CXXGraph/Utility/Typedef.hpp index 90186bc86..f0ce56d45 100755 --- a/include/CXXGraph/Utility/Typedef.hpp +++ b/include/CXXGraph/Utility/Typedef.hpp @@ -22,8 +22,6 @@ #pragma once -#include -#include #include #include #include @@ -36,12 +34,8 @@ namespace CXXGraph { // Smart pointers alias template -using unique = std::unique_ptr; -template using shared = std::shared_ptr; -using std::make_shared; -using std::make_unique; template class Node; @@ -244,8 +238,6 @@ struct BestFirstSearchResult_struct { template using BestFirstSearchResult = BestFirstSearchResult_struct; -/// Struct that contains the information about the partitioning statistics - /////////////////////////////////////////////////////////////////////////////////// // Using Definition // /////////////////////////////////////////////////////////////// diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 31abf0e0c..74c4c0434 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,7 +41,7 @@ if(TEST) OPTIONS "CMAKE_POSITION_INDEPENDENT_CODE True" ) - file (GLOB TEST_FILES "*.cpp" "*.hpp") + file (GLOB TEST_FILES "*.cpp" "*.hpp" "*.h") add_executable(test_exe ${TEST_FILES}) target_compile_definitions(test_exe diff --git a/test/GraphTest.cpp b/test/GraphTest.cpp index a475d0a59..f613ebaf3 100644 --- a/test/GraphTest.cpp +++ b/test/GraphTest.cpp @@ -1,7 +1,4 @@ -#include -#include -#include #include #include "CXXGraph/CXXGraph.hpp" diff --git a/test/PartitionTest.cpp b/test/PartitionTest.cpp index 572e71ca4..cb6fb924e 100644 --- a/test/PartitionTest.cpp +++ b/test/PartitionTest.cpp @@ -1,4 +1,5 @@ #include + #include "CXXGraph/CXXGraph.hpp" #include "Utilities.hpp" #include "gtest/gtest.h" @@ -7,10 +8,10 @@ template using unique = std::unique_ptr; template -using shared= std::shared_ptr; +using shared = std::shared_ptr; -using std::make_unique; using std::make_shared; +using std::make_unique; static auto nodes = generateRandomNodes(10000, 2); static auto edges = generateRandomEdges(10000, nodes); @@ -53,8 +54,8 @@ TEST(PartitionTest, test_1) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -73,8 +74,8 @@ TEST(PartitionTest, test_2) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::HDRF_ALG, 4, 1, 0.001); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -126,8 +127,9 @@ TEST(PartitionTest, test_3) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, + 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -146,8 +148,9 @@ TEST(PartitionTest, test_4) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EDGEBALANCED_VC_ALG, + 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -199,8 +202,8 @@ TEST(PartitionTest, test_5) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -219,8 +222,8 @@ TEST(PartitionTest, test_6) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::GREEDY_VC_ALG, 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -272,8 +275,8 @@ TEST(PartitionTest, test_7) { edgeSet.insert(make_shared>(edge12)); CXXGraph::Graph graph(edgeSet); ASSERT_EQ(graph.getEdgeSet().size(), 12); - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -292,8 +295,8 @@ TEST(PartitionTest, test_8) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = graph.partitionGraph( - CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::EBV_ALG, 4, 1, 1); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size(); @@ -312,9 +315,9 @@ TEST(PartitionTest, test_9) { for (auto e : edges) { graph.addEdge(e.second); } - auto partitionMap = - graph.partitionGraph(CXXGraph::Partitioning::PartitionAlgorithm::WB_LIBRA, - 4, 1.0, 0.0, 0.0, 4); + auto partitionMap = CXXGraph::Partitioning::Partitioner::partitionGraph( + graph, CXXGraph::Partitioning::PartitionAlgorithm::WB_LIBRA, 4, 1.0, 0.0, + 0.0, 4); size_t totalEdgeInPartition = 0; for (const auto &elem : partitionMap) { totalEdgeInPartition += elem.second->getEdgeSet().size();