Skip to content

Commit

Permalink
added visualization for graphs
Browse files Browse the repository at this point in the history
Doubly linked lists test cases are failing. Need review asap.
  • Loading branch information
spirosmaggioros committed Jan 21, 2024
1 parent b3826ef commit 73ec963
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 15 deletions.
11 changes: 7 additions & 4 deletions examples/graph/graph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@
#endif

int main() {
weighted_graph<int> g("directed", {{{1, 3}, {2, 4}}, {{3, 4}, {4, 5}}});
weighted_graph<int> g("undirected");
g.add_edge(1, 3, 4);
g.add_edge(3, 4, 5);
g.add_edge(0, 1, 6);
g.add_edge(0, 2, 7);

std::cout << g.shortest_path(0, 10) << '\n';

graph<int> g2("directed", {{1, 3}, {3, 4}, {4, 5}});
graph<int> g2("directed", { {1, {2,3}}, {2, {4, 5}}});
std::vector<int> v = g2.bfs(0);
for (auto &x : v) {
std::cout << x << " ";
}
std::cout << '\n';

graph<char> g3("directed", { {'a', 'b'}, {'c', 'd'}, {'b','c'} });
std::cout << "Graph g3 is: " << g3 << '\n';

// now you can visualize graphs with algoplus
g2.visualize();
}
7 changes: 7 additions & 0 deletions examples/graph/unnamed.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
digraph G{
2->4
2->5
1->2
1->3

}
2 changes: 1 addition & 1 deletion examples/list/doubly_list_iteration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ int main() {
for (doubly_list_iter<int> it = l1.begin(); it != l1.end(); it++) {
std::cout << *(it) << '\n';
}
}
}
155 changes: 147 additions & 8 deletions src/classes/graph/graph.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#ifndef GRAPH_H
#define GRAPH_H

#include "../../visualization/graph_visual/graph_visualization.h"

#ifdef __cplusplus
#include <iostream>
#include <queue>
#include <stack>
#include <type_traits>
#include <string>
#include <unordered_map>
#include <unordered_set>
Expand All @@ -13,17 +16,17 @@

template <typename T> class graph {
public:
graph(std::string __type, std::vector<std::vector<T>> __adj = {}) {
graph(std::string __type, std::vector<std::pair<T, std::vector<T> > > __adj = {}) {
try {
if (__type == "directed" || __type == "undirected") {
this->__type = __type;
} else {
throw std::invalid_argument("Can't recognize the type of graph");
}
if (!__adj.empty()) {
for (size_t i = 0; i < __adj.size(); i++) {
for (T &x : __adj[i]) {
this->add_edge(i, x);
for(size_t i = 0; i<__adj.size(); i++){
for(T &neigh: __adj[i].second){
this -> add_edge(__adj[i].first, neigh);
}
}
}
Expand Down Expand Up @@ -67,6 +70,8 @@ template <typename T> class graph {
std::vector<T> topological_sort();

bool bipartite();

void visualize();

friend std::ostream & operator <<(std::ostream &out, graph<T> &g){
out << '{';
Expand Down Expand Up @@ -258,10 +263,65 @@ template <typename T> bool graph<T>::bipartite(){
}


template <typename T> void graph<T>::visualize(){
std::string s;
if(__type == "directed"){
if(std::is_same_v<T, char> || std::is_same_v<T, std::string>){
for(auto &[element, neighbors]: adj){
for(T &x : neighbors){
s += element;
s += "->";
s += x;
s += '\n';
}
}
}
else{
for(auto &[element, neighbors]: adj){
for(T &x : neighbors){
s += std::to_string(element);
s += "->";
s += std::to_string(x);
s += '\n';
}
}
}
}
else{
if(std::is_same_v<T, char> || std::is_same_v<T, std::string>){
for(auto &[element, neighbors]: adj){
for(T &x : neighbors){
s += element;
s += "--";
s += x;
s += '\n';
}
}
}
else{
for(auto &[element, neighbors]: adj){
for(T &x : neighbors){
s += std::to_string(element);
s += "--";
s += std::to_string(x);
s += '\n';
}
}
}
}
s += '\n';
if(__type == "directed"){
digraph_visualization::visualize(s);
}
else{
graph_visualization::visualize(s);
}
}

template <typename T> class weighted_graph {
public:
weighted_graph(std::string __type,
std::vector<std::vector<std::pair<T, int64_t>>> __adj = {{}}) {
std::vector<std::pair<std::pair<T,T>, int64_t>> __adj = {}) {
try {
if (__type == "directed" || __type == "undirected") {
this->__type = __type;
Expand All @@ -270,9 +330,7 @@ template <typename T> class weighted_graph {
}
if (!__adj.empty()) {
for (size_t i = 0; i < __adj.size(); i++) {
for (std::pair<T, int64_t> &x : __adj[i]) {
this->add_edge(i, x.first, x.second);
}
this->add_edge(__adj[i].first.first, __adj[i].first.second, __adj[i].second);
}
}
} catch (std::invalid_argument &e) {
Expand Down Expand Up @@ -320,6 +378,8 @@ template <typename T> class weighted_graph {

bool bipartite();

void visualize();

friend std::ostream &operator <<(std::ostream &out, weighted_graph<T> &g){
out << '{';
std::vector<T> elements = g.topological_sort();
Expand Down Expand Up @@ -596,4 +656,83 @@ template <typename T> bool weighted_graph<T>::bipartite(){
return true;
}

template <typename T> void weighted_graph<T>::visualize(){
std::string s;
if(__type == "directed"){
if(std::is_same_v<T, char> || std::is_same_v<T, std::string>){
for(auto &[element, neighbors]: adj){
for(std::pair<T, int64_t> &x : neighbors){
if(x.first == element){
continue;
}
s += element;
s += "->";
s += x.first;
s += "[weight=";
s += std::to_string(x.second);
s += "]";
s += '\n';
}
}
}
else{
for(auto &[element, neighbors]: adj){
for(std::pair<T, int64_t> &x : neighbors){
if(x.first == element){
continue;
}
s += std::to_string(element);
s += "->";
s += std::to_string(x.first);
s += "[weight=";
s += std::to_string(x.second);
s += "]";
s += '\n';
}
}
}
}
else{
if(std::is_same_v<T, char> || std::is_same_v<T, std::string>){
for(auto &[element, neighbors]: adj){
for(std::pair<T, int64_t> &x : neighbors){
if(x.first == element){
continue;
}
s += element;
s += "--";
s += x.first;
s += "[weight=";
s += std::to_string(x.second);
s += "]";
s += '\n';
}
}
}
else{
for(auto &[element, neighbors]: adj){
for(std::pair<T,int64_t> &x : neighbors){
if(x.first == element){
continue;
}
s += std::to_string(element);
s += "--";
s += std::to_string(x.first);
s += "[weight=";
s += std::to_string(x.second);
s += "]";
s += '\n';
}
}
}
}
if(__type == "directed"){
digraph_visualization::visualize(s);
}
else{
graph_visualization::visualize(s);
}

}

#endif
2 changes: 1 addition & 1 deletion src/classes/list/doubly_linked_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ template <typename T> class doubly_linked_list {
p->prev() = nullptr;
return p;
}
};
};
116 changes: 116 additions & 0 deletions src/visualization/graph_visual/graph_visualization.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <string>
#include <iostream>
#include <fstream>

using namespace std;

#define OPEN_COMMAND "open"

// COLORS FOR TEXT
const char* reset = "\033[0m";
const char* black = "\033[30m";
const char* red = "\033[31m";
const char* green = "\033[32m";
const char* yellow = "\033[33m";
const char* blue = "\033[34m";
const char* magenta = "\033[35m";
const char* cyan = "\033[36m";
const char* white = "\033[37m";

const char* blackbackground = "\033[40m";
const char* redbackground = "\033[41m";
const char* greenbackground = "\033[42m";
const char* yellowbackground = "\033[43m";
const char* bluebackground = "\033[44m";
const char* magentabackground = "\033[45m";
const char* cyanbackground = "\033[46m";
const char* whitebackground = "\033[47m";

const char* bold = "\033[1m";
const char* underline = "\033[4m";
const char* blink = "\033[5m";
const char* inverse = "\033[7m";


namespace graph_visualization{

void visualize(std::string &__generate, std::string newFileName = "unnamed.dot") {
auto start_time = std::chrono::high_resolution_clock::now();
try {
if (newFileName.size() < 5 || newFileName.substr(newFileName.length() - 4) != ".dot") newFileName += ".dot";
//newFileName = "examples/" + newFileName;
// Open the file for writing
std::ofstream outFile(newFileName);

// Check if the file is successfully opened
if (outFile.is_open()) {
// Write the DOT format header
outFile << "graph G{" << std::endl;

// Generate DOT code recursively
outFile << __generate;
// Write the DOT format footer
outFile << "}" << std::endl;

// Close the file
outFile.close();

std::cout << green << "Visualization file '" << newFileName << "' created successfully." << std::endl << reset;
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
double runtime_sec = static_cast<double>(duration.count()) / 1e6;

std::cout << blue << "Visualization runtime: " << runtime_sec << " sec" << std::endl << reset;
std::string openCommand = OPEN_COMMAND + std::string(" ") + newFileName;
system(openCommand.c_str());
} else {
std::cerr << red << "Error: Unable to open file '" << newFileName << "' for writing." << std::endl << reset;
}
} catch (const std::exception& e) {
std::cerr << red << "Error: " << e.what() << std::endl << reset;
}
};

};


namespace digraph_visualization{

void visualize(std::string &__generate, std::string newFileName = "unnamed.dot") {
auto start_time = std::chrono::high_resolution_clock::now();
try {
if (newFileName.size() < 5 || newFileName.substr(newFileName.length() - 4) != ".dot") newFileName += ".dot";
//newFileName = "examples/" + newFileName;
// Open the file for writing
std::ofstream outFile(newFileName);

// Check if the file is successfully opened
if (outFile.is_open()) {
// Write the DOT format header
outFile << "digraph G{" << std::endl;

// Generate DOT code recursively
outFile << __generate;
// Write the DOT format footer
outFile << "}" << std::endl;

// Close the file
outFile.close();

std::cout << green << "Visualization file '" << newFileName << "' created successfully." << std::endl << reset;
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
double runtime_sec = static_cast<double>(duration.count()) / 1e6;

std::cout << blue << "Visualization runtime: " << runtime_sec << " sec" << std::endl << reset;
std::string openCommand = OPEN_COMMAND + std::string(" ") + newFileName;
system(openCommand.c_str());
} else {
std::cerr << red << "Error: Unable to open file '" << newFileName << "' for writing." << std::endl << reset;
}
} catch (const std::exception& e) {
std::cerr << red << "Error: " << e.what() << std::endl << reset;
}
};

};
2 changes: 1 addition & 1 deletion tests/graph/graph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <string>

TEST_CASE("testing clearing of a graph"){
graph<int> g("directed", { {1,2}, {3,4}, {4,5}});
graph<int> g("directed", { {0, {1,2}}, {1, {3,4}}, {2, {4,5}}});
g.clear();
std::vector<int> v = g.topological_sort();
std::vector<int> v2 = g.dfs(0);
Expand Down

0 comments on commit 73ec963

Please sign in to comment.