From 7ae6db5be131ad6dfd197ff0badba19ecea720b4 Mon Sep 17 00:00:00 2001 From: Stefan Pranger Date: Tue, 15 Feb 2022 08:18:33 +0100 Subject: [PATCH] added uniqueness checks for min cut/max flow --- Graph.cpp | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++--- Graph.h | 23 ++++++- main.cpp | 9 ++- 3 files changed, 204 insertions(+), 9 deletions(-) diff --git a/Graph.cpp b/Graph.cpp index 34c9d54..d8e892e 100644 --- a/Graph.cpp +++ b/Graph.cpp @@ -1,12 +1,11 @@ #include #include #include -#include #include +#include #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) { @@ -33,6 +32,7 @@ namespace data { for(auto const &arc : m_arc_list) { m_capapcities.at(arc.start - 1).at(arc.end - 1) += arc.capacity; } + m_network = m_capapcities; } 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(); printInformation(); do { @@ -53,6 +53,7 @@ namespace data { if(m_verbose_max_flow) printMaxFlowInformation(); if(m_min_cut) printMinCut(); if(m_verbosity >= 1) printComputationStatistics(start, std::chrono::steady_clock::now()); + return m_max_flow; } int Graph::findAugmentingPaths() { @@ -135,6 +136,108 @@ namespace data { 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 *recursive_stack = new std::stack(); + 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(); + 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 *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) <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 *stack) { + int first, previous, current; + + std::stack 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 *cut_edges = new std::vector(); + 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 { 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); }); @@ -162,15 +265,41 @@ namespace data { } } - void Graph::printMinCut() const { - std::vector min_cut, complement; + void Graph::computeMinCut(std::vector *source_vertices, std::vector *sink_vertices, std::vector *cut_edges) const { + if(!source_vertices) source_vertices = new std::vector(); + if(!sink_vertices) sink_vertices = new std::vector(); for(auto const &vertex : m_vertices) { if(vertex.getLevel() != UNDEF_LEVEL) { - min_cut.push_back(std::to_string(vertex.getID())); + source_vertices->push_back(vertex.getID()); } 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 *arcs = new std::vector(); + std::vector *source_vertices = new std::vector(); + std::vector *sink_vertices = new std::vector(); + computeMinCut(source_vertices, sink_vertices, arcs); + std::vector 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: {"; bool first = true; for(auto const &v : min_cut) { @@ -196,4 +325,42 @@ namespace data { } *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; + } } diff --git a/Graph.h b/Graph.h index e55e168..491d2a8 100644 --- a/Graph.h +++ b/Graph.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -12,6 +13,12 @@ #define NO_AUGMENTING_PATH_FOUND -1 +#define INVALID_VERTEX -1 +#define NOT_PROCESSED 0 +#define ON_STACK 1 +#define PROCESSED 2 + + typedef std::vector> CapacityMatrix; namespace data { @@ -22,7 +29,10 @@ namespace data { void parseFromString(const std::string &graph_string); void parseFromFile(const std::string &graph_file); - void maxFlowDinic(); + int maxFlowDinic(); + void printMatrices() const; + void hasUniqueMaxFlow(); + void hasUniqueMinCut(); private: void initMatrices(); @@ -33,11 +43,21 @@ namespace data { void buildPath(std::vector ¤t_path); void computeFlowForPath(const std::vector ¤t_path); + void isResidualGraphCyclic(const CapacityMatrix &residual_graph, int *visited, const int previous, std::stack *recursive_stack, bool &found_cycle); + void printCycle(const CapacityMatrix residual_matrix, std::stack *stack); + + void computeMinCut(std::vector *source_vertices = nullptr, std::vector *sink_vertices = nullptr, std::vector *cut_edges = nullptr) const; + void incrementArcCapacity(const int source, const int sink); + 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; + void disableOutput(); + void enableOutput(); + void resetNetwork(); + std::vector m_vertices; std::vector m_arc_list; VertexID m_source_id; @@ -45,6 +65,7 @@ namespace data { std::vector::iterator m_source; std::vector::iterator m_sink; + CapacityMatrix m_network; CapacityMatrix m_flow; CapacityMatrix m_capapcities; diff --git a/main.cpp b/main.cpp index 090293f..652444e 100644 --- a/main.cpp +++ b/main.cpp @@ -14,7 +14,9 @@ int main(int argc, char* argv[]) { auto stdout_output = optionParser.add("o", "stdout", "Output to stdout."); auto file_output = optionParser.add, popl::Attribute::optional>("p", "output-file", "Filename for output."); - auto verbose_max_flow = optionParser.add("a", "max-flow", "Include verbose information about the max flow to the output."); + auto uniqueness = optionParser.add("u", "check-uniqueness", "Check uniqueness of maximum flow and minimum cut."); + + auto verbose_max_flow = optionParser.add("a", "max-flow", "Include verbose information about the maximum flow to the output."); auto min_cut_option = optionParser.add("m", "minimum-cut", "Include the minimum cut set to the output."); auto verbose_option = optionParser.add("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()) { network.parseFromString(input_string->value(0)); } + network.maxFlowDinic(); + if(uniqueness->is_set()) { + network.hasUniqueMaxFlow(); + network.hasUniqueMinCut(); + } return 0; }