Browse Source

added uniqueness checks for min cut/max flow

main
Stefan Pranger 3 years ago
parent
commit
7ae6db5be1
  1. 181
      Graph.cpp
  2. 23
      Graph.h
  3. 9
      main.cpp

181
Graph.cpp

@ -1,12 +1,11 @@
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <queue> #include <queue>
#include <stack>
#include <sstream> #include <sstream>
#include <climits>
#include "Graph.h" #include "Graph.h"
#include "util/GraphParser.h" #include "util/GraphParser.h"
namespace data { namespace data {
Graph::Graph(bool stdout_output, bool file_output, std::string output_filename, bool verbose_max_flow, bool min_cut, int verbosity) 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) { : 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) {
@ -33,6 +32,7 @@ namespace data {
for(auto const &arc : m_arc_list) { for(auto const &arc : m_arc_list) {
m_capapcities.at(arc.start - 1).at(arc.end - 1) += arc.capacity; m_capapcities.at(arc.start - 1).at(arc.end - 1) += arc.capacity;
} }
m_network = m_capapcities;
} }
void Graph::initOstream() { void Graph::initOstream() {
@ -43,7 +43,7 @@ namespace data {
} }
} }
void Graph::maxFlowDinic() {
int Graph::maxFlowDinic() {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
printInformation(); printInformation();
do { do {
@ -53,6 +53,7 @@ namespace data {
if(m_verbose_max_flow) printMaxFlowInformation(); if(m_verbose_max_flow) printMaxFlowInformation();
if(m_min_cut) printMinCut(); if(m_min_cut) printMinCut();
if(m_verbosity >= 1) printComputationStatistics(start, std::chrono::steady_clock::now()); if(m_verbosity >= 1) printComputationStatistics(start, std::chrono::steady_clock::now());
return m_max_flow;
} }
int Graph::findAugmentingPaths() { int Graph::findAugmentingPaths() {
@ -135,6 +136,108 @@ namespace data {
if(m_verbosity >= 1) m_num_level_graphs_built++; if(m_verbosity >= 1) m_num_level_graphs_built++;
} }
void Graph::hasUniqueMaxFlow() {
*m_ofstream << "\nChecking uniqueness of maximum flow:\n";
CapacityMatrix residualGraph = m_capapcities;
for(uint row = 0; row < m_flow.size(); row++) {
for(uint i = 0; i < m_flow.at(0).size(); i++) {
residualGraph.at(i).at(row) += m_flow.at(row).at(i);
}
}
int visited[m_num_vertices];
std::stack<int> *recursive_stack = new std::stack<int>();
bool found_cycle = false;
for(uint i = 0; i < m_num_vertices; i++){
visited[i] = NOT_PROCESSED;
}
for(uint i = 0; i < m_num_vertices; i++){
if(visited[i] == NOT_PROCESSED) {
visited[i] = ON_STACK;
recursive_stack = new std::stack<int>();
recursive_stack->push(i);
isResidualGraphCyclic(residualGraph, visited, INVALID_VERTEX, recursive_stack, found_cycle);
}
}
if(!found_cycle) *m_ofstream << "The max flow is unique!\n";
}
void Graph::isResidualGraphCyclic(const CapacityMatrix &residual_graph, int *visited, const int previous, std::stack<int> *recursive_stack, bool &found_cycle) {
int top = recursive_stack->top();
for(uint next = 0; next < residual_graph.at(top).size(); next++) {
if(next == previous) continue;
if(residual_graph.at(top).at(next) == 0) continue;
//std::cout << "prev, top, next, capacity: " << previous +1 << ", " << top +1 << ", " << next+1 << ", " << residual_graph.at(top).at(next) <<std::endl;
if(visited[next] == NOT_PROCESSED || visited[next] == PROCESSED) {
recursive_stack->push(next);
visited[next] = ON_STACK;
isResidualGraphCyclic(residual_graph, visited, top, recursive_stack, found_cycle);
} else if(visited[next] == ON_STACK) {
if(recursive_stack->size() >= 3) {
found_cycle = true;
printCycle(residual_graph, recursive_stack);
}
}
}
visited[top] = PROCESSED;
recursive_stack->pop();
}
void Graph::printCycle(const CapacityMatrix residual_matrix, std::stack<int> *stack) {
int first, previous, current;
std::stack<int> print_stack;
while(stack->size() != 0) {
print_stack.push(stack->top());
stack->pop();
}
previous = print_stack.top();
first = previous;
print_stack.pop();
*m_ofstream << previous + 1;
int min_capacity = INT_MAX;
while(print_stack.size() != 0) {
current = print_stack.top();
*m_ofstream << " -> " << current + 1;
if(residual_matrix.at(previous).at(current) < min_capacity) min_capacity = residual_matrix.at(previous).at(current);
print_stack.pop();
stack->push(current);
previous = current;
}
*m_ofstream << " -> " << first + 1;
if(residual_matrix.at(current).at(first) < min_capacity) min_capacity = residual_matrix.at(current).at(first);
*m_ofstream << ", where " << min_capacity << " unit";
if(min_capacity > 1) *m_ofstream << "s";
*m_ofstream << " of flow could be shifted." << std::endl;
}
void Graph::hasUniqueMinCut() {
*m_ofstream << "\nChecking uniqueness of minimum cut:\n";
std::vector<Arc> *cut_edges = new std::vector<Arc>();
computeMinCut(nullptr, nullptr, cut_edges);
bool found_another_min_cut = false;
for(auto const &arc : *cut_edges) {
Graph augmented_network = *this;
augmented_network.incrementArcCapacity(arc.start - 1, arc.end - 1);
augmented_network.resetNetwork();
augmented_network.disableOutput();
int augmented_max_flow = augmented_network.maxFlowDinic();
augmented_network.enableOutput();
if(augmented_max_flow == m_max_flow) {
found_another_min_cut = true;
*m_ofstream << "Found another minimum cut after incrementing (" << arc.start << "," << arc.end << ") with a flow value of " << augmented_max_flow << "." << std::endl;
augmented_network.printMinCut();
//augmented_network.hasUniqueMinCut();
}
}
if(!found_another_min_cut) {
*m_ofstream << "The minimum cut is unique!\n";
}
}
void Graph::printInformation() const { 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_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); }); auto m_sink = std::find_if(m_vertices.begin(), m_vertices.end(), [this] (const Vertex &v) { return (v.getID() == m_sink_id); });
@ -162,15 +265,41 @@ namespace data {
} }
} }
void Graph::printMinCut() const {
std::vector<std::string> min_cut, complement;
void Graph::computeMinCut(std::vector<Vertex> *source_vertices, std::vector<Vertex> *sink_vertices, std::vector<Arc> *cut_edges) const {
if(!source_vertices) source_vertices = new std::vector<Vertex>();
if(!sink_vertices) sink_vertices = new std::vector<Vertex>();
for(auto const &vertex : m_vertices) { for(auto const &vertex : m_vertices) {
if(vertex.getLevel() != UNDEF_LEVEL) { if(vertex.getLevel() != UNDEF_LEVEL) {
min_cut.push_back(std::to_string(vertex.getID()));
source_vertices->push_back(vertex.getID());
} else { } else {
complement.push_back(std::to_string(vertex.getID()));
sink_vertices->push_back(vertex.getID());
}
}
if(cut_edges) {
for(auto const source : *source_vertices) {
for(auto const sink : *sink_vertices) {
int capacity = m_network.at(source.getID() - 1).at(sink.getID() - 1);
if(capacity > 0) {
cut_edges->push_back({source.getID(), sink.getID(), capacity, capacity, 0});
}
}
}
}
}
void Graph::printMinCut() const {
std::vector<Arc> *arcs = new std::vector<Arc>();
std::vector<Vertex> *source_vertices = new std::vector<Vertex>();
std::vector<Vertex> *sink_vertices = new std::vector<Vertex>();
computeMinCut(source_vertices, sink_vertices, arcs);
std::vector<std::string> min_cut, complement;
for(auto const &vertex : *source_vertices) {
min_cut.push_back(std::to_string(vertex.getID()));
} }
for(auto const &vertex : *sink_vertices) {
complement.push_back(std::to_string(vertex.getID()));
} }
*m_ofstream << "Min Cut X: {"; *m_ofstream << "Min Cut X: {";
bool first = true; bool first = true;
for(auto const &v : min_cut) { for(auto const &v : min_cut) {
@ -196,4 +325,42 @@ namespace data {
} }
*m_ofstream << " #recursive buildPath calls: " << m_num_build_path_calls << "\n"; *m_ofstream << " #recursive buildPath calls: " << m_num_build_path_calls << "\n";
} }
void Graph::incrementArcCapacity(const int source, const int sink) {
m_network.at(source).at(sink)++;
}
void Graph::resetNetwork() {
m_max_flow = 0;
m_capapcities = m_network;
}
void Graph::disableOutput() {
m_ofstream->setstate(std::ios_base::badbit);
}
void Graph::enableOutput() {
m_ofstream->clear();
}
void Graph::printMatrices() const {
//for(auto const row : m_flow) {
// for(auto const i : row) {
// std::cout << i << " ";
// } std::cout << std::endl;
//}
std::cout << std::endl;
for(auto const row : m_network) {
for(auto const i : row) {
std::cout << i << " ";
} std::cout << std::endl;
}
std::cout << std::endl;
for(auto const row : m_capapcities) {
for(auto const i : row) {
std::cout << i << " ";
} std::cout << std::endl;
}
std::cout << std::endl;
}
} }

23
Graph.h

@ -3,6 +3,7 @@
#include <chrono> #include <chrono>
#include <vector> #include <vector>
#include <set> #include <set>
#include <stack>
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
@ -12,6 +13,12 @@
#define NO_AUGMENTING_PATH_FOUND -1 #define NO_AUGMENTING_PATH_FOUND -1
#define INVALID_VERTEX -1
#define NOT_PROCESSED 0
#define ON_STACK 1
#define PROCESSED 2
typedef std::vector<std::vector<Capacity>> CapacityMatrix; typedef std::vector<std::vector<Capacity>> CapacityMatrix;
namespace data { namespace data {
@ -22,7 +29,10 @@ namespace data {
void parseFromString(const std::string &graph_string); void parseFromString(const std::string &graph_string);
void parseFromFile(const std::string &graph_file); void parseFromFile(const std::string &graph_file);
void maxFlowDinic();
int maxFlowDinic();
void printMatrices() const;
void hasUniqueMaxFlow();
void hasUniqueMinCut();
private: private:
void initMatrices(); void initMatrices();
@ -33,11 +43,21 @@ namespace data {
void buildPath(std::vector<Vertex> &current_path); void buildPath(std::vector<Vertex> &current_path);
void computeFlowForPath(const std::vector<Vertex> &current_path); void computeFlowForPath(const std::vector<Vertex> &current_path);
void isResidualGraphCyclic(const CapacityMatrix &residual_graph, int *visited, const int previous, std::stack<int> *recursive_stack, bool &found_cycle);
void printCycle(const CapacityMatrix residual_matrix, std::stack<int> *stack);
void computeMinCut(std::vector<Vertex> *source_vertices = nullptr, std::vector<Vertex> *sink_vertices = nullptr, std::vector<Arc> *cut_edges = nullptr) const;
void incrementArcCapacity(const int source, const int sink);
void printInformation() const; void printInformation() const;
void printMaxFlowInformation() const; void printMaxFlowInformation() const;
void printMinCut() const; void printMinCut() const;
void printComputationStatistics(const std::chrono::steady_clock::time_point &start, const std::chrono::steady_clock::time_point &end) const; void printComputationStatistics(const std::chrono::steady_clock::time_point &start, const std::chrono::steady_clock::time_point &end) const;
void disableOutput();
void enableOutput();
void resetNetwork();
std::vector<Vertex> m_vertices; std::vector<Vertex> m_vertices;
std::vector<Arc> m_arc_list; std::vector<Arc> m_arc_list;
VertexID m_source_id; VertexID m_source_id;
@ -45,6 +65,7 @@ namespace data {
std::vector<Vertex>::iterator m_source; std::vector<Vertex>::iterator m_source;
std::vector<Vertex>::iterator m_sink; std::vector<Vertex>::iterator m_sink;
CapacityMatrix m_network;
CapacityMatrix m_flow; CapacityMatrix m_flow;
CapacityMatrix m_capapcities; CapacityMatrix m_capapcities;

9
main.cpp

@ -14,7 +14,9 @@ int main(int argc, char* argv[]) {
auto stdout_output = optionParser.add<popl::Switch, popl::Attribute::optional>("o", "stdout", "Output to stdout."); 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 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 uniqueness = optionParser.add<popl::Switch, popl::Attribute::optional>("u", "check-uniqueness", "Check uniqueness of maximum flow and minimum cut.");
auto verbose_max_flow = optionParser.add<popl::Switch, popl::Attribute::optional>("a", "max-flow", "Include verbose information about the maximum 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 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 information about all augmenting paths computed."); auto verbose_option = optionParser.add<popl::Switch, popl::Attribute::optional>("v", "verbose", "Output verbose algorithmic information and runtime. Pass twice to get information about all augmenting paths computed.");
@ -51,7 +53,12 @@ int main(int argc, char* argv[]) {
} else if (input_string->count()) { } else if (input_string->count()) {
network.parseFromString(input_string->value(0)); network.parseFromString(input_string->value(0));
} }
network.maxFlowDinic(); network.maxFlowDinic();
if(uniqueness->is_set()) {
network.hasUniqueMaxFlow();
network.hasUniqueMinCut();
}
return 0; return 0;
} }
Loading…
Cancel
Save