Browse Source

initial commit

Finished basic implementation of Dinic's algorithm
main
Stefan Pranger 2 years ago
commit
bc203ea9c5
  1. 1
      .gitignore
  2. 27
      CMakeLists.txt
  3. 211
      Graph.cpp
  4. 70
      Graph.h
  5. 57
      main.cpp
  6. 13
      util/Arc.h
  7. 7
      util/CMakeLists.txt
  8. 76
      util/GraphParser.cpp
  9. 16
      util/GraphParser.h
  10. 54
      util/OptionParser.cpp
  11. 15
      util/OptionParser.h
  12. 35
      util/Vertex.cpp
  13. 32
      util/Vertex.h
  14. 1331
      util/popl.hpp

1
.gitignore

@ -0,0 +1 @@
build/

27
CMakeLists.txt

@ -0,0 +1,27 @@
include(util/CMakeLists.txt)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
#add_definitions(-DLOG_DEBUG)
cmake_minimum_required(VERSION 3.0...3.22)
set(CMAKE_BUILD_TYPE Debug)
project(
max_flow
VERSION 1.0
LANGUAGES CXX)
#find_package(Boost COMPONENTS program_options REQUIRED)
#include_directories("." db exceptions)
add_executable(maxFlow
${SRCS}
main.cpp
Graph.cpp
)
target_link_libraries(maxFlow stdc++fs)

211
Graph.cpp

@ -0,0 +1,211 @@
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <sstream>
#include "Graph.h"
#include "util/GraphParser.h"
namespace data {
Graph::Graph(bool stdout_output, bool file_output, std::string output_filename, bool verbose_max_flow, bool min_cut, int verbosity)
: m_file_output(file_output), m_output_file_name(output_filename), m_verbose_max_flow(verbose_max_flow), m_min_cut(min_cut), m_verbosity(verbosity) {
//if(!stdout_output && file_output) {
// m_stdout_output = false;
//} else {
// m_stdout_output = true;
//}
}
void Graph::parseFromString(const std::string &graph_string) {
parser::parseString(graph_string, m_arc_list, m_vertices, m_source_id, m_sink_id, m_num_vertices, m_num_arcs);
setSourceAndSinkIterator();
initMatrices();
initOstream();
}
void Graph::parseFromFile(const std::string &graph_file) {
if(graph_file == m_output_file_name) {
throw std::runtime_error("Input graph file name and output file name are the same. Will not overwrite. Exiting...");
}
parser::parseFile(graph_file, m_arc_list, m_vertices, m_source_id, m_sink_id, m_num_vertices, m_num_arcs);
setSourceAndSinkIterator();
initMatrices();
initOstream();
}
void Graph::initMatrices() {
m_flow.resize(m_num_vertices, std::vector<Capacity>(m_num_vertices, 0));
m_capapcities.resize(m_num_vertices, std::vector<Capacity>(m_num_vertices, 0));
for(auto const &arc : m_arc_list) {
m_capapcities.at(arc.start - 1).at(arc.end - 1) = arc.capacity; // how to best map arbitrary ids to index in matrix
}
}
void Graph::setSourceAndSinkIterator() {
auto m_source = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return (v.getID() == m_source_id); });
auto m_sink = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return (v.getID() == m_sink_id); });
}
void Graph::initOstream() {
if(m_file_output) {
m_ofstream = new std::ofstream(m_output_file_name);
} else {
m_ofstream = &std::cout;
}
}
void Graph::maxFlowDinic() {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
printInformation();
do {
constructLevelGraph();
} while(findAugmentingPaths() != NO_AUGMENTING_PATH_FOUND);
*m_ofstream << "Found max flow |x| = " << m_max_flow << "\n";
if(m_verbose_max_flow) printMaxFlowInformation();
if(m_min_cut) printMinCut();
if(m_verbosity >= 1) printComputationStatistics(start, std::chrono::steady_clock::now());
}
int Graph::findAugmentingPaths() {
auto m_sink = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return v.getID() == m_sink_id; });
if(m_sink->getLevel() == UNDEF_LEVEL) {
return NO_AUGMENTING_PATH_FOUND;
}
for(auto &v : m_vertices) {
v.setVisited(false);
}
auto m_source = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return v.getID() == m_source_id; });
std::vector<Vertex> path{*m_source};
buildPath(path);
return 0;
}
void Graph::buildPath(std::vector<Vertex> &current_path) {
Vertex head = current_path.back();
if(head.getID() == m_sink_id) {
computeFlowForPath(current_path);
}
for(auto const& arc : head.getOutgoingArcs()) {
if(m_capapcities.at(arc.start - 1).at(arc.end - 1) <= 0) continue;
auto it = std::find_if(m_vertices.begin(), m_vertices.end(), [&arc] (const Vertex &v) { return v.getID() == arc.end; });
if(head.getLevel() + 1 != it->getLevel()) continue;
if(it != m_vertices.end()) {
current_path.push_back(*it);
buildPath(current_path);
}
current_path.pop_back();
}
if(m_verbosity >= 1) m_num_build_path_calls++;
}
void Graph::computeFlowForPath(const std::vector<Vertex> &current_path) {
std::vector<Capacity> path_capacities;
for(uint i = 0; i < current_path.size() - 1; i++) {
path_capacities.push_back(m_capapcities.at(current_path.at(i).getID() - 1).at(current_path.at(i + 1).getID() - 1));
}
Capacity flow = *std::min_element(path_capacities.begin(), path_capacities.end());
m_max_flow += flow;
for(uint i = 0; i < current_path.size() - 1; i++) {
m_capapcities.at(current_path.at(i).getID() - 1).at(current_path.at(i + 1).getID() - 1) -= flow;
m_flow.at(current_path.at(i).getID() - 1).at(current_path.at(i + 1).getID() - 1) += flow;
}
if(m_verbosity >= 1) m_num_paths++;
if(m_verbosity >= 2) {
std::stringstream path;
path << std::to_string(current_path.front().getID());
for(uint i = 1; i < current_path.size(); i++) {
path << " > " << current_path.at(i).getID();
}
path << " | flow = " << flow;
m_augmenting_paths.push_back(path.str());
}
}
void Graph::constructLevelGraph() {
std::queue<Vertex> q;
for(auto &v : m_vertices) {
v.setLevel(UNDEF_LEVEL);
}
auto m_source = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return (v.getID() == m_source_id); });
m_source->setLevel(0);
q.push(*m_source);
while(!q.empty()) {
Vertex current_vertex = q.front();
int current_level = current_vertex.getLevel();
q.pop();
// restructure this to use matrix
for(auto const &arc : current_vertex.getOutgoingArcs()) {
if(m_capapcities.at(arc.start - 1).at(arc.end - 1) <= 0) continue;
auto it = std::find_if(m_vertices.begin(), m_vertices.end(), [&arc] (const Vertex &v) { return (v.getID() == arc.end) && !v.hasDefinedLevel(); });
if(it != m_vertices.end()) {
it->setLevel(current_level + 1);
q.push(*it);
}
}
}
if(m_verbosity >= 1) m_num_level_graphs_built++;
}
void Graph::printInformation() const {
auto m_source = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return (v.getID() == m_source_id); });
auto m_sink = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return (v.getID() == m_sink_id); });
*m_ofstream << "#Vertices: " << m_num_vertices << std::endl;
*m_ofstream << "#Arc: " << m_num_arcs << std::endl;
*m_ofstream << "Source: " << m_source->getID() << ", Sink: " << m_sink->getID() << std::endl;
*m_ofstream << "Vertices: ";
bool first = true;
for(auto const& v : m_vertices) {
if(first) first = false;
else *m_ofstream << ", ";
*m_ofstream << v.getID();
}
*m_ofstream << std::endl;
for(auto const& a : m_arc_list) {
*m_ofstream << " " << a.start << " -> " << a.end << " capacity = " << a.capacity << std::endl;
}
*m_ofstream << std::endl;
}
void Graph::printMaxFlowInformation() const {
*m_ofstream << "Max Flow per arc:\n";
for(auto const &arc : m_arc_list) {
*m_ofstream << " " << arc.start << " -> " << arc.end << " flow = " << m_flow.at(arc.start - 1 ).at(arc.end - 1) << "/" << arc.capacity << "\n";
}
}
void Graph::printMinCut() const {
std::vector<std::string> min_cut, complement;
for(auto const &vertex : m_vertices) {
if(vertex.getLevel() != UNDEF_LEVEL) {
min_cut.push_back(std::to_string(vertex.getID()));
} else {
complement.push_back(std::to_string(vertex.getID()));
}
}
*m_ofstream << "Min Cut X: {";
bool first = true;
for(auto const &v : min_cut) {
if(first) first = false;
else *m_ofstream << ", ";
*m_ofstream << v;
} *m_ofstream << "}\nComplement(X): {";
first = true;
for(auto const &v : complement) {
if(first) first = false;
else *m_ofstream << ", ";
*m_ofstream << v;
} *m_ofstream << "}\n";
}
void Graph::printComputationStatistics(const std::chrono::steady_clock::time_point &start, const std::chrono::steady_clock::time_point &end) const {
*m_ofstream << "Elapsed time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms (" << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << "µs).\n";
*m_ofstream << "Computation Statistics:\n";
*m_ofstream << " #level graphs built: " << m_num_level_graphs_built << "\n";
*m_ofstream << " #augmenting paths computed: " << m_num_paths << "\n";
if(m_verbosity >= 2) {
for(auto const &path : m_augmenting_paths) *m_ofstream << " " << path << "\n";
}
*m_ofstream << " #recursive buildPath calls: " << m_num_build_path_calls << "\n";
}
}

70
Graph.h

@ -0,0 +1,70 @@
#pragma once
#include <chrono>
#include <vector>
#include <set>
#include <string>
#include <fstream>
#include <iostream>
#include "util/GraphParser.h"
#include "util/Arc.h"
#define NO_AUGMENTING_PATH_FOUND -1
typedef std::vector<std::vector<Capacity>> CapacityMatrix;
namespace data {
class Graph {
public:
Graph(bool stdout_output, bool file_output, std::string output_filename, bool verbose_max_flow, bool min_cut, int verbosity);
void parseFromString(const std::string &graph_string);
void parseFromFile(const std::string &graph_file);
void maxFlowDinic();
private:
void initMatrices();
void setSourceAndSinkIterator();
void initOstream();
void constructLevelGraph();
int findAugmentingPaths();
void buildPath(std::vector<Vertex> &current_path);
void computeFlowForPath(const std::vector<Vertex> &current_path);
void printInformation() const;
void printMaxFlowInformation() const;
void printMinCut() const;
void printComputationStatistics(const std::chrono::steady_clock::time_point &start, const std::chrono::steady_clock::time_point &end) const;
std::vector<Vertex> m_vertices;
std::vector<Arc> m_arc_list;
VertexID m_source_id;
VertexID m_sink_id;
std::vector<Vertex>::iterator m_source;
std::vector<Vertex>::iterator m_sink;
CapacityMatrix m_flow;
CapacityMatrix m_capapcities;
int m_num_vertices;
int m_num_arcs;
int m_max_flow = 0;
bool m_stdout_output = true;
bool m_file_output = false;
std::string m_output_file_name;
std::ostream *m_ofstream;
bool m_verbose_max_flow = false;
bool m_min_cut = false;
int m_verbosity = 0;
uint m_num_paths = 0;
uint m_num_build_path_calls = 0;
uint m_num_level_graphs_built = 0;
std::vector<std::string> m_augmenting_paths;
};
}

57
main.cpp

@ -0,0 +1,57 @@
#include <iostream>
#include <string>
#include "util/OptionParser.h"
#include "Graph.h"
int main(int argc, char* argv[]) {
popl::OptionParser optionParser("Allowed options");
auto help_option = optionParser.add<popl::Switch>("h", "help", "Print this help message.");
auto input_filename = optionParser.add<popl::Value<std::string>, popl::Attribute::optional>("f", "input-file", "Filename of input graph file.");
auto input_string = optionParser.add<popl::Value<std::string>, popl::Attribute::optional>("s", "input-string", "Input graph string.");
auto stdout_output = optionParser.add<popl::Switch, popl::Attribute::optional>("o", "stdout", "Output to stdout.");
auto file_output = optionParser.add<popl::Value<std::string>, popl::Attribute::optional>("p", "output-file", "Filename for output.");
auto verbose_max_flow = optionParser.add<popl::Switch, popl::Attribute::optional>("a", "max-flow", "Include verbose information about the max flow to the output.");
auto min_cut_option = optionParser.add<popl::Switch, popl::Attribute::optional>("m", "minimum-cut", "Include the minimum cut set to the output.");
auto verbose_option = optionParser.add<popl::Switch, popl::Attribute::optional>("v", "verbose", "Output verbose algorithmic information and runtime. Pass twice to get step-by-step information.");
try {
optionParser.parse(argc, argv);
if(parser::checkOption(input_filename->count(), input_string->count()) > 0) {
std::cout << optionParser << std::endl;
return EXIT_FAILURE;
}
if(help_option->count() > 0) {
std::cout << optionParser << std::endl;
}
} catch (const popl::invalid_option &e) {
return parser::printPoplException(e);
} catch (const std::exception &e) {
std::cerr << "Exception: " << e.what() << "\n";
return EXIT_FAILURE;
}
std::string filename = "";
if(file_output->is_set()) {
filename = file_output->value(0);
}
data::Graph network(stdout_output->is_set(),
file_output->is_set(),
filename,
verbose_max_flow->is_set(),
min_cut_option->is_set(),
verbose_option->count());
if(input_filename->count() > 0) {
network.parseFromFile(input_filename->value(0));
} else if (input_string->count()) {
network.parseFromString(input_string->value(0));
}
network.maxFlowDinic();
return 0;
}

13
util/Arc.h

@ -0,0 +1,13 @@
#pragma once
typedef int VertexID;
typedef int Capacity;
typedef int Flow;
typedef struct Arc {
VertexID start;
VertexID end;
Capacity capacity;
Capacity residual_capacity; // might not be needed
Flow flow; // might not be needed
} Arc;

7
util/CMakeLists.txt

@ -0,0 +1,7 @@
list(APPEND SRCS
${CMAKE_CURRENT_LIST_DIR}/Arc.h
${CMAKE_CURRENT_LIST_DIR}/GraphParser.cpp
${CMAKE_CURRENT_LIST_DIR}/OptionParser.cpp
${CMAKE_CURRENT_LIST_DIR}/Vertex.cpp
${CMAKE_CURRENT_LIST_DIR}/popl.hpp
)

76
util/GraphParser.cpp

@ -0,0 +1,76 @@
#include <algorithm>
#include <fstream>
#include <iostream> // only debug
#include <sstream>
#include "GraphParser.h"
#include "Vertex.h"
void parseGraphInformation(const std::string &line, int &num_vertices, int &num_arcs, VertexID &source, VertexID &sink) {
int num;
std::vector<int> information;
std::istringstream ss(line);
for (; ss;) {
if (ss >> num) {
information.push_back(num);
} else if (!ss.eof()) {
ss.clear();
ss.ignore(1);
}
}
if(information.size() != 4) {
std::cerr << "The graph information on line 0 is not well defined:\n";
std::cerr << line << std::endl;
throw std::runtime_error("The graph information on line 0 is not well defined.");
}
num_vertices = information.at(0);
num_arcs = information.at(1);
source = information.at(2);
sink = information.at(3);
}
Arc parseArc(const std::string &line, const int &line_count) {
try {
int start, dest, capacity;
std::istringstream(line) >> start >> dest >> capacity;
return Arc{start, dest, capacity, capacity, 0}; // check sanity here
} catch(const std::exception &e) {
std::cerr << "Error on line " << line_count << ":\t" << line << std::endl;
throw std::runtime_error("Error parsing the arc on line " + std::to_string(line_count));
}
}
namespace parser {
void parseString(const std::string &graph_string, std::vector<Arc> &arc_list, std::vector<Vertex> &vertices, VertexID &source_id, VertexID &sink_id, int &num_vertices, int &num_arcs) {
int line_count = 0;
std::set<Vertex> vertex_set;
std::stringstream stream(graph_string);
std::string line;
std::getline(stream, line);
parseGraphInformation(line, num_vertices, num_arcs, source_id, sink_id);
line_count++;
while (std::getline(stream, line)) {
Arc arc = parseArc(line, line_count);
arc_list.push_back(arc);
vertex_set.insert(Vertex{arc.start});
vertex_set.insert(Vertex{arc.end});
line_count++;
}
vertices.resize(vertex_set.size());
std::copy(vertex_set.begin(), vertex_set.end(), vertices.begin());
for(auto &arc : arc_list) {
auto it = std::find_if(vertices.begin(), vertices.end(), [&arc] (const Vertex &v) { return v.getID() == arc.start; }); // check if capacity is > 0
it->addOutgoingArc(arc);
}
}
void parseFile(const std::string &graph_filename, std::vector<Arc> &m_arc_list, std::vector<Vertex> &m_vertices, VertexID &source_id, VertexID &sink_id, int &m_num_vertices, int &m_num_arcs) {
std::ifstream input_file(graph_filename);
std::string content((std::istreambuf_iterator<char>(input_file)), (std::istreambuf_iterator<char>()));
parseString(content, m_arc_list, m_vertices, source_id, sink_id, m_num_vertices, m_num_arcs);
}
}

16
util/GraphParser.h

@ -0,0 +1,16 @@
#pragma once
#include <set>
#include <string>
#include <vector>
#include "Vertex.h"
#include "Arc.h"
class Vertex;
namespace parser {
void parseString(const std::string &graph_string, std::vector<Arc> &m_arc_list, std::vector<Vertex> &m_vertices, VertexID &source_id, VertexID &sink_id, int &m_num_vertices, int &m_num_arcs);
void parseFile(const std::string &graph_filename, std::vector<Arc> &m_arc_list, std::vector<Vertex> &m_vertices, VertexID &source_id, VertexID &sink_id, int &m_num_vertices, int &m_num_arcs);
}

54
util/OptionParser.cpp

@ -0,0 +1,54 @@
#include <iostream>
#include "popl.hpp"
#include "OptionParser.h"
namespace parser {
int printPoplException(const popl::invalid_option &e) {
std::cerr << "Invalid Option Exception: " << e.what() << "\n";
std::cerr << "error: ";
if (e.error() == popl::invalid_option::Error::missing_argument) {
std::cerr << "missing_argument\n";
} else if (e.error() == popl::invalid_option::Error::invalid_argument) {
std::cerr << "invalid_argument\n";
} else if (e.error() == popl::invalid_option::Error::too_many_arguments) {
std::cerr << "too_many_arguments\n";
} else if (e.error() == popl::invalid_option::Error::missing_option) {
std::cerr << "missing_option\n";
}
if (e.error() == popl::invalid_option::Error::missing_option) {
std::string option_name(e.option()->name(popl::OptionName::short_name, true));
if (option_name.empty())
option_name = e.option()->name(popl::OptionName::long_name, true);
std::cerr << "option: " << option_name << "\n";
}
else {
std::cerr << "option: " << e.option()->name(e.what_name()) << "\n";
std::cerr << "value: " << e.value() << "\n";
}
return EXIT_FAILURE;
}
int checkOption(const int input_filename_count,
const int input_string_count) {
if(input_filename_count > 1) {
std::cerr << "You may only pass one input graph file.";
return INPUT_ERROR;
}
if(input_string_count > 1) {
std::cerr << "You may only pass one input graph string.";
return INPUT_ERROR;
}
if(input_filename_count > 0 && input_string_count > 0) {
std::cerr << "You may only pass either an input graph file or an input graph string.";
return INPUT_ERROR;
}
if(input_filename_count == 0 && input_string_count == 0) {
std::cerr << "You need to pass either and input graph file or an input graph string.";
return INPUT_ERROR;
}
return INPUT_OK;
}
}

15
util/OptionParser.h

@ -0,0 +1,15 @@
#pragma once
#include <iostream>
#include "popl.hpp"
#define INPUT_ERROR 1
#define INPUT_OK 0
namespace parser {
int printPoplException(const popl::invalid_option &e);
int checkOption(const int input_filename_count,
const int input_string_count);
}

35
util/Vertex.cpp

@ -0,0 +1,35 @@
#include "Vertex.h"
Vertex::Vertex() {}
Vertex::Vertex(const int &id) : m_id(id) {}
VertexID Vertex::getID() const {
return m_id;
}
std::vector<Arc> Vertex::getOutgoingArcs() const {
return m_outgoing_arcs;
}
int Vertex::getLevel() const {
return m_level;
}
bool Vertex::visited() const {
return m_visited;
}
bool Vertex::hasDefinedLevel() const {
return m_level != UNDEF_LEVEL;
}
void Vertex::addOutgoingArc(const Arc &arc) {
m_outgoing_arcs.push_back(arc);
}
void Vertex::setLevel(const int &level) {
m_level = level;
}
void Vertex::setVisited(const bool &visited) {
m_visited = visited;
}

32
util/Vertex.h

@ -0,0 +1,32 @@
#pragma once
#include "Arc.h"
#include "GraphParser.h"
#define UNDEF_LEVEL -1
class Vertex {
public:
Vertex();
Vertex(const int &id);
VertexID getID() const;
std::vector<Arc> getOutgoingArcs() const;
int getLevel() const;
bool visited() const;
bool hasDefinedLevel() const;
void addOutgoingArc(const Arc &arc);
void setLevel(const int &level);
void setVisited(const bool &visited = true);
private:
VertexID m_id;
int m_level;
bool m_visited;
std::vector<Arc> m_outgoing_arcs;
};
inline bool operator<(const Vertex& lhs, const Vertex& rhs) {
return lhs.getID() < rhs.getID();
}

1331
util/popl.hpp
File diff suppressed because it is too large
View File

Loading…
Cancel
Save