Integrate Kolmogorov's max flow algorithm, from Stephan Diederich.

[SVN r36125]
This commit is contained in:
Douglas Gregor 2006-11-20 23:42:44 +00:00
parent a86397fc34
commit 1cfa23497b
23 changed files with 36829 additions and 12 deletions

View File

@ -405,6 +405,15 @@ Morgan Kaufmann Publishers, San Fransisco, 1997.
<em>Modern Compiler Implementation in JAVA</em><br>
Cambridge University Press, 1998.
<p></p><dt><a name="kolmogorov03">68</a>
<dd>Vladimir Kolmogorov<br>
<em>Graph Based Algorithms for Scene Reconstruction from Two or More Views</em><br>
PhD thesis, Cornell University, September 2003.
<p></p><dt><a name="boykov-kolmogorov04">69</a>
<dd>Yuri Boykov and Vladimir Kolmogorov<br>
<em><a href="http://www.csd.uwo.ca/faculty/yuri/Abstracts/pami04-abs.html">An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision</a></em><br>
In IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 26, no. 9, pp. 1124-1137, Sept. 2004.
</dl>
<br>

View File

@ -79,6 +79,8 @@ This algorithm provides a very simple and easy to implement solution to
the maximum flow problem. However, there are several reasons why this
algorithm is not as good as the <a
href="./push_relabel_max_flow.html"><tt>push_relabel_max_flow()</tt></a>
or the <a
href="./kolmogorov_max_flow.html"><tt>kolmogorov_max_flow()</tt></a>
algorithm.
<ul>
@ -218,7 +220,8 @@ from a file in the DIMACS format and computes the maximum flow.
<h3>See Also</h3>
<a href="./push_relabel_max_flow.html"><tt>push_relabel_max_flow()</tt></a>.
<a href="./push_relabel_max_flow.html"><tt>push_relabel_max_flow()</tt></a><br>
<a href="./kolmogorov_max_flow.html"><tt>kolmogorov_max_flow()</tt></a>.
<br>
<HR>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -80,6 +80,12 @@ September 27, 2000.
<h2>Changes by version</h2>
<a name="by-version">
<ul>
<a name="1.35.0"></a><li>Version 1.35.0<br><b>New algorithms and components</b>
<ul>
<li><a href="kolmogorov_max_flow.html"><tt>kolmogorov_max_flow</tt></a>, from Stephan Diederich as part of the <a href="http://code.google.com/soc/">2006 Google Summer of Code</a>.</li>
<li><a href="read_dimacs.html">read_dimacs</a> and <a href="write_dimacs.html">write_dimacs</a> for max-flow problems, from Stephan Diederich.</li>
</ul>
</li><br>
<a name="1.34.0"></a><li>Version 1.34.0<br><b>New algorithms and components</b>
<ul>
<li><a href="maximum_matching.html"><tt>edmonds_maximum_cardinality_matching</tt></a>, from Aaron Windsor.</li>

View File

@ -0,0 +1,384 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-15">
<TITLE>Boost Graph Library: Kolmogorov Maximum Flow</TITLE>
<META NAME="GENERATOR" CONTENT="OpenOffice.org 2.0 (Linux)">
<META NAME="CREATED" CONTENT="20060820;17315200">
<META NAME="CHANGEDBY" CONTENT="Stephan Diederich">
<META NAME="CHANGED" CONTENT="20060820;23125100">
<!--
// Copyright (c) 2006, Stephan Diederich
//
// This documentation may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
-->
<STYLE>
<!--
TD P { color: #000000 }
H1 { color: #000000 }
P { color: #000000 }
PRE { color: #000000 }
H3 { color: #000000 }
BLOCKQUOTE { color: #000000 }
A:link { color: #0000ee }
A:visited { color: #551a8b }
-->
</STYLE>
</HEAD>
<BODY LANG="de-DE" TEXT="#000000" LINK="#0000ee" VLINK="#551a8b" BGCOLOR="#ffffff" DIR="LTR">
<P><IMG SRC="../../../boost.png" NAME="Grafik1" ALT="C++ Boost" ALIGN=BOTTOM WIDTH=277 HEIGHT=86 BORDER=0>
</P>
<H1><A NAME="sec:kolmogorov_max_flow"></A><TT>kolmogorov_max_flow</TT>
</H1>
<PRE><I>// named parameter version</I>
template &lt;class Graph, class P, class T, class R&gt;
typename property_traits&lt;typename property_map&lt;Graph, edge_capacity_t&gt;::const_type&gt;::value_type
kolmogorov_max_flow(Graph&amp; g,
typename graph_traits&lt;Graph&gt;::vertex_descriptor src,
typename graph_traits&lt;Graph&gt;::vertex_descriptor sink,
const bgl_named_params&lt;P, T, R&gt;&amp; params = <I>all defaults</I>)
<I>// non-named parameter version</I>
template &lt;class Graph, class CapacityEdgeMap, class ResidualCapacityEdgeMap, class ReverseEdgeMap,
class PredecessorMap, class ColorMap, class DistanceMap, class IndexMap&gt;
typename property_traits&lt;CapacityEdgeMap&gt;::value_type
kolmogorov_max_flow(Graph&amp; g,
CapacityEdgeMap cap,
ResidualCapacityEdgeMap res_cap,
ReverseEdgeMap rev_map,
PredecessorMap pre_map,
ColorMap color,
DistanceMap dist,
IndexMap idx,
typename graph_traits &lt;Graph&gt;::vertex_descriptor src,
typename graph_traits &lt;Graph &gt;::vertex_descriptor sink)</PRE><P>
<FONT SIZE=3>Additional overloaded versions for non-named parameters
are provided (without DistanceMap/ColorMap/DistanceMap; for those
iterator_property_maps with the provided index map are used)</FONT></P>
<P>The <TT>kolmogorov_max_flow()</TT> function calculates the maximum
flow of a network. See Section <A HREF="graph_theory_review.html#sec:network-flow-algorithms">Network
Flow Algorithms</A> for a description of maximum flow. The calculated
maximum flow will be the return value of the function. The function
also calculates the flow values <I>f(u,v)</I> for all <I>(u,v)</I> in
<I>E</I>, which are returned in the form of the residual capacity
<I>r(u,v) = c(u,v) - f(u,v)</I>.
</P>
<P><B>Requirements:</B><BR>The directed graph <I>G=(V,E)</I> that
represents the network must include a reverse edge for every edge in
<I>E</I>. That is, the input graph should be <I>G<SUB>in</SUB> =
(V,{E U E<SUP>T</SUP>})</I>. The <TT>ReverseEdgeMap</TT> argument <TT>rev</TT>
must map each edge in the original graph to its reverse edge, that is
<I>(u,v) -&gt; (v,u)</I> for all <I>(u,v)</I> in <I>E</I>.
</P>
<P>Remarks: While the push-relabel method states that each edge in <I>E<SUP>T</SUP></I>
has to have capacity of 0, the reverse edges for this algorithm ARE
allowed to carry capacities. If there are already reverse edges in
the input Graph <I><FONT FACE="Courier New, monospace">G</FONT></I>,
those can be used. This can halve the amount of edges and will
noticeably increase the performance.<BR><BR><B>Algorithm
description:</B><BR>Kolmogorov's algorithm is a variety of the
augmenting-path algorithm. Standard augmenting path algorithms find
shortest paths from source to sink vertex and augment them by
substracting the bottleneck capacity found on that path from the
residual capacities of each edge and adding it to the total flow.
Additionally the minimum capacity is added to the residual capacity
of the reverse edges. If no more paths in the residual-edge tree are
found, the algorithm terminates. Instead of finding a new shortest
path from source to sink in the graph in each iteration, Kolmogorov's
version keeps the already found paths as follows:</P>
<P>The algorithm builds up two search trees, a source-tree and a
sink-tree. Each vertex has a label (stored in <I>ColorMap</I>) to
which tree it belongs and a status-flag if this vertex is active or
passive. In the beginning of the algorithm only the source and the
sink are colored (source==white, sink==black) and have active status.
All other vertices are colored gray. The algorithm consists of three
phases:</P>
<P><I>grow-phase</I>: In this phase active vertices are allowed to
acquire neighbor vertices that are connected through an edge that has
a capacity-value greater than zero. Acquiring means that those vertices
become active and belong now to the search tree of the current
active vertex. If there are no more valid connections to neighbor
vertices, the current vertex becomes passive and the grow phase
continues with the next active vertex. The grow phase terminates if
there are no more active vertices left or a vertex discovers a vertex
from the other search tree through an unsaturated edge. In this case
a path from source to sink is found.</P>
<P><I>augment-phase</I>: This phase augments the path that was found
in the grow phase. First it finds the bottleneck capacity of the
found path, and then it updates the residual-capacity of the edges
from this path by substracting the bottleneck capacity from the
residual capacity. Furthermore the residual capacity of the reverse
edges are updated by adding the bottleneck capacity. This phase can
destroy the built up search trees, as it creates at least one
saturated edge. That means, that the search trees collapse to
forests, because a condition for the search trees is, that each
vertex in them has a valid (=non-saturated) connection to a terminal.</P>
<P><I>adoption-phase</I>: Here the search trees are reconstructed. A
simple solution would be to mark all vertices coming after the first
orphan in the found path free vertices (gray). A more sophisticated
solution is to give those orphans new parents: The neighbor vertices
are checked if they have a valid connection to the same terminal like
this vertex had (a path with unsaturated edges). If there is one,
this vertex becomes the new parent of the current orphan and this
forest is re-included into the search tree. If no new valid parent is
found, this vertex becomes a free vertex (marked gray), and it's
children become orphans. The adoption phase terminates if there are
no more orphans.</P>
<P><IMG SRC="figs/kolmogorov_max_flow.gif" NAME="Grafik2" ALIGN=LEFT WIDTH=827 HEIGHT=311 BORDER=0><BR CLEAR=LEFT><B>Details:</B></P>
<UL>
<LI><P>Marking heuristics: A timestamp is stored for each vertex
which shows in which iteration of the algorithm the distance to the
corresponding terminal was calculated.
</P>
<UL>
<LI><P>This distance is used and gets calculated in the
adoption-phase. In order to find a valid new parent for an orphan,
the possible parent is checked for a connection to the terminal to
which tree it belongs. If there is such a connection, the path is
tagged with the current time-stamp, and the distance value. If
another orphan has to find a parent and it comes across a vertex
with a current timestamp, this information is used.</P>
<LI><P>The distance is also used in the grow-phase. If a vertex
comes across another vertex of the same tree while searching for
new vertices, the other's distance is compared to its distance. If
it is smaller, that other vertex becomes the new parent of the
current. This can decrease the length of the search paths, and so
amount of adoptions.</P>
</UL>
<LI><P>Ordering of orphans: As described above, the augment-phase
and the adoption phase can create orphans. The orphans the
augment-phase generates, are ordered according to their distance to
the terminals (smallest first). This combined with the
distance/timestamp heuristics results in the possibility for not
having to recheck terminal-connections too often. New orphans which
are generated in adoption phase are processed before orphans from
the main queue for the same reason.</P>
</UL>
<P><BR><B>Implementation notes:</B></P>
<P>The algorithm is mainly implemented as described in the PhD thesis
of Kolmogorov. Few changes were made for increasing performance:</P>
<UL>
<LI><P>initialization: the algorithm first augments all paths from
source-&gt;sink and all paths from source-&gt;VERTEX-&gt;sink. This
improves especially graph-cuts used in image vision where nearly
each vertex has a source and sink connect. During this step, all
vertices that have an unsaturated connection from source are added
to the active vertex list and so the source is not.
</P>
<LI><P>active vertices: Kolmogorov uses two lists for active nodes
and states that new active vertices are added to the rear of the
second. Fetching an active vertex is done from the beginning of the
first list. If the first list is empty, it is exchanged by the
second. This implementation uses just one list.</P>
<LI><P>grow-phase: In the grow phase the first vertex in the
active-list is taken and all outgoing edges are checked if they are
unsaturated. This decreases performance for graphs with high-edge
density. This implementation stores the last accessed edge and
continues with it, if the first vertex in the active-list is the
same one as during the last grow-phase.</P>
</UL>
<P>This algorithm [<A HREF="bibliography.html#kolmogorov03">68</a>, <a href="bibliography.html#boykov-kolmogorov04">69</a>] was developed by Boykov and Kolmogorov.
</P>
<H3>Where Defined</H3>
<P><TT><A HREF="../../../boost/graph/kolmogorov_max_flow.hpp">boost/graph/kolmogorov_max_flow.hpp</A></TT>
</P>
<H3>Parameters</H3>
<P>IN: <TT>Graph&amp; g</TT>
</P>
<BLOCKQUOTE>A directed graph. The graph's type must be a model of
<A HREF="VertexListGraph.html">Vertex List Graph</A>, <A HREF="EdgeListGraph.html">Edge
List Graph</A> and <A HREF="IncidenceGraph.html">Incidence Graph</A>.
For each edge <I>(u,v)</I> in the graph, the reverse edge <I>(v,u)</I>
must also be in the graph.
</BLOCKQUOTE>
<P>IN: <TT>vertex_descriptor src</TT>
</P>
<BLOCKQUOTE>The source vertex for the flow network graph.
</BLOCKQUOTE>
<P>IN: <TT>vertex_descriptor sink</TT>
</P>
<BLOCKQUOTE>The sink vertex for the flow network graph.
</BLOCKQUOTE>
<H3>Named Parameters</H3>
<P>IN: <TT>capacity_map(EdgeCapacityMap cap)</TT>
</P>
<BLOCKQUOTE>The edge capacity property map. The type must be a model
of a constant <A HREF="../../property_map/LvaluePropertyMap.html">Lvalue
Property Map</A>. The key type of the map must be the graph's edge
descriptor type.<BR><B>Default:</B> <TT>get(edge_capacity, g)</TT>
</BLOCKQUOTE>
<P>OUT: <TT>residual_capacity_map(ResidualCapacityEdgeMap res)</TT>
</P>
<BLOCKQUOTE>The edge residual capacity property map. The type must be
a model of a mutable <A HREF="../../property_map/LvaluePropertyMap.html">Lvalue
Property Map</A>. The key type of the map must be the graph's edge
descriptor type.<BR><B>Default:</B> <TT>get(edge_residual_capacity,
g)</TT>
</BLOCKQUOTE>
<P>IN: <TT>reverse_edge_map(ReverseEdgeMap rev)</TT>
</P>
<BLOCKQUOTE>An edge property map that maps every edge <I>(u,v)</I> in
the graph to the reverse edge <I>(v,u)</I>. The map must be a model
of constant <A HREF="../../property_map/LvaluePropertyMap.html">Lvalue
Property Map</A>. The key type of the map must be the graph's edge
descriptor type.<BR><B>Default:</B> <TT>get(edge_reverse, g)</TT>
</BLOCKQUOTE>
<P>UTIL: <TT>vertex_predecessor(PredecessorMap pre_map)</TT>
</P>
<BLOCKQUOTE>A vertex property map that stores the edge to the vertex'
predecessor. The map must be a model of mutable <A HREF="../../property_map/LvaluePropertyMap.html">Lvalue
Property Map</A>. The key type of the map must be the graph's vertex
descriptor type.<BR><B>Default:</B> <TT>get(vertex_predecessor, g)</TT>
</BLOCKQUOTE>
<P>OUT/UTIL: <TT>vertex_color(ColorMap color)</TT>
</P>
<BLOCKQUOTE>A vertex property map that stores a color for edge
vertex. If the color of a vertex after running the algorithm is white
the vertex belongs to the source tree else it belongs to the
sink-tree (used for minimum cuts). The map must be a model of mutable
<A HREF="../../property_map/LvaluePropertyMap.html">Lvalue Property
Map</A>. The key type of the map must be the graph's vertex
descriptor type.<BR><B>Default:</B> <TT>get(vertex_color, g)</TT>
</BLOCKQUOTE>
<P>UTIL: <TT>vertex_distance(DistanceMap dist)</TT>
</P>
<BLOCKQUOTE>A vertex property map that stores the distance to the
corresponding terminal. It's a utility-map for speeding up the
algorithm. The map must be a model of mutable <A HREF="../../property_map/LvaluePropertyMap.html">Lvalue
Property Map</A>. The key type of the map must be the graph's vertex
descriptor type.<BR><B>Default:</B> <TT>get(vertex_distance, g)</TT>
</BLOCKQUOTE>
<P>IN: <TT>vertex_index_map(VertexIndexMap index_map)</TT>
</P>
<BLOCKQUOTE>Maps each vertex of the graph to a unique integer in the
range <TT>[0, num_vertices(g))</TT>. The map must be a model of
constant <A HREF="../../property_map/LvaluePropertyMap.html">LvaluePropertyMap</A>.
The key type of the map must be the graph's vertex descriptor
type.<BR><B>Default:</B> <TT>get(vertex_index, g)</TT>
</BLOCKQUOTE>
<H3>Example</H3>
<P>This reads an example maximum flow problem (a graph with edge
capacities) from a file in the DIMACS format (<TT><A HREF="../example/max_flow.dat">example/max_flow.dat</A></TT>).
The source for this example can be found in
<TT><A HREF="../example/kolmogorov-eg.cpp">example/kolmogorov-eg.cpp</A></TT>.
</P>
<PRE>#include &lt;boost/config.hpp&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;boost/graph/kolmogorov_map_flow.hpp&gt;
#include &lt;boost/graph/adjacency_list.hpp&gt;
#include &lt;boost/graph/read_dimacs.hpp&gt;
int
main()
{
using namespace boost;
typedef adjacency_list_traits&lt;vecS, vecS, directedS&gt; Traits;
typedef adjacency_list&lt;vecS, vecS, directedS,
property&lt;vertex_name_t, std::string&gt;,
property&lt;edge_capacity_t, long,
property&lt;edge_residual_capacity_t, long,
property&lt;edge_reverse_t, Traits::edge_descriptor&gt; &gt; &gt;
&gt; Graph;
Graph g;
long flow;
property_map&lt;Graph, edge_capacity_t&gt;::type
capacity = get(edge_capacity, g);
property_map&lt;Graph, edge_reverse_t&gt;::type
rev = get(edge_reverse, g);
property_map&lt;Graph, edge_residual_capacity_t&gt;::type
residual_capacity = get(edge_residual_capacity, g);
Traits::vertex_descriptor s, t;
read_dimacs_max_flow(g, capacity, rev, s, t);
flow = kolmogorov_max_flow(g, s, t);
std::cout &lt;&lt; &quot;c The total flow:&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; &quot;s &quot; &lt;&lt; flow &lt;&lt; std::endl &lt;&lt; std::endl;
std::cout &lt;&lt; &quot;c flow values:&quot; &lt;&lt; std::endl;
graph_traits&lt;Graph&gt;::vertex_iterator u_iter, u_end;
graph_traits&lt;Graph&gt;::out_edge_iterator ei, e_end;
for (tie(u_iter, u_end) = vertices(g); u_iter != u_end; ++u_iter)
for (tie(ei, e_end) = out_edges(*u_iter, g); ei != e_end; ++ei)
if (capacity[*ei] &gt; 0)
std::cout &lt;&lt; &quot;f &quot; &lt;&lt; *u_iter &lt;&lt; &quot; &quot; &lt;&lt; target(*ei, g) &lt;&lt; &quot; &quot;
&lt;&lt; (capacity[*ei] - residual_capacity[*ei]) &lt;&lt; std::endl;
return 0;
}</PRE><P>
The output is:
</P>
<PRE>c The total flow:
s 13
c flow values:
f 0 6 3
f 0 1 0
f 0 2 10
f 1 5 1
f 1 0 0
f 1 3 0
f 2 4 4
f 2 3 6
f 2 0 0
f 3 7 5
f 3 2 0
f 3 1 1
f 4 5 4
f 4 6 0
f 5 4 0
f 5 7 5
f 6 7 3
f 6 4 0
f 7 6 0
f 7 5 0</PRE><H3>
See Also</H3>
<P STYLE="margin-bottom: 0cm"><TT><A HREF="edmunds_karp_max_flow.html">edmunds_karp_max_flow()</A></TT>,<BR><TT><A HREF="push_relabel_max_flow.html">push_relabel_max_flow()</A></TT>.
</P>
<HR>
<TABLE CELLPADDING=2 CELLSPACING=2>
<TR VALIGN=TOP>
<TD>
<P>Copyright &copy; 2006</P>
</TD>
<TD>
<P>Stephan Diederich, University
Mannheim(<A HREF="mailto:diederich@ti.uni-manheim.de">diederich@ti.uni-manheim.de</A>)</P>
</TD>
</TR>
</TABLE>
<P><BR><BR>
</P>
</BODY>
</HTML>

View File

@ -229,7 +229,8 @@ f 6 7 1
<h3>See Also</h3>
<a href="./edmunds_karp_max_flow.html"><tt>edmunds_karp_max_flow()</tt></a>.
<a href="./edmunds_karp_max_flow.html"><tt>edmunds_karp_max_flow()</tt></a><br>
<a href="./kolmogorov_max_flow.html"><tt>kolmogorov_max_flow()</tt></a>.
<br>
<HR>

99
doc/read_dimacs.html Normal file
View File

@ -0,0 +1,99 @@
<HTML>
<!--
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
-->
<Head>
<Title>Boost Graph Library: read_dimacs_max_flow</Title>
<BODY BGCOLOR="#ffffff" LINK="#0000ee" TEXT="#000000" VLINK="#551a8b"
ALINK="#ff0000">
<IMG SRC="../../../boost.png"
ALT="C++ Boost" width="277" height="86">
<BR Clear>
<H1><A NAME="sec:read_dimacs_max_flow">
<TT>read_dimacs_max_flow</TT>
</H1>
<pre>
//reads a graph with attached edge_capacity properties from an std::istream
template &lt;class Graph, class CapacityMap, class ReverseEdgeMap&gt;
int read_dimacs_max_flow(Graph& g,
CapacityMap capacity,
ReverseEdgeMap reverse_edge,
typename graph_traits<Graph>::vertex_descriptor& src,
typename graph_traits<Graph>::vertex_descriptor& sink,
std::istream& in=std::cin)
</pre>
<p>
This method reads a BGL graph object from a max-flow problem description in extended dimacs format. (see <a href="http://www.avglab.com/andrew/CATS/maxflow_formats.htm"><TT>Goldbergs site</TT></a> for more information). For each edge found in the
file an additional reverse_edge is added and set in the reverse_edge map. Source- and sink-vertex-descriptors are set according to the dimacs file.
<H3>Where Defined</H3>
<P>
<a href="../../../boost/graph/read_dimacs.hpp"><TT>boost/graph/read_dimacs.hpp</TT></a>
<h3>Parameters</h3>
IN: <tt>Graph&amp; g</tt>
<blockquote>
A directed or undirected graph. The graph's type must be a model of <a href="./IncidenceGraph.html">IncidenceGraph</a>.
</blockquote>
OUT: <tt>CapacityMap capacity</tt>
<blockquote>
A property map that models <a href="../../property_map/LvaluePropertyMap.html">mutable Lvalue Property Map</a> whose key type is the edge descriptor of the graph. <br>
</blockquote>
OUT: <tt>ReverseEdgeMap reverse_edge</tt>
<blockquote>
A property map that models <a href="../../property_map/LvaluePropertyMap.html">mutable Lvalue Property Map</a> whose key and value type is the edge descriptor of the graph. This map stores the corresponding reverse edge for each each in Graph g.<br>
</blockquote>
IN: <tt>std::istream&amp; in</tt>
<blockquote>
A standard <tt>std::istream</tt> object. <br>
<b>Default</b>: <tt>std::cin (for backward compatibility)</tt>
</blockquote>
<H3>
Example
</H3>
A short <a href="../example/read_write_dimacs-eg.cpp">example</a> which uses read_dimacs and write_dimacs is located in the examples directory.
<h3>See Also</h3>
<a href="./write_dimacs.html"><tt>write_dimacs</tt></a>
</BODY>
</HTML>

View File

@ -174,6 +174,7 @@
<OL>
<LI><A href="./edmunds_karp_max_flow.html"><tt>edmunds_karp_max_flow</tt></A>
<LI><A href="./push_relabel_max_flow.html"><tt>push_relabel_max_flow</tt></A>
<li><a href="kolmogorov_max_flow.html"><tt>kolmogorov_max_flow</tt></a></li>
<LI><A href="./maximum_matching.html"><tt>edmonds_maximum_cardinality_matching</tt></A>
</OL>
@ -217,11 +218,11 @@
</OL>
</OL>
<LI>AT&amp;T Graphviz Read/Write Utilities
<OL>
<LI><a href="./write-graphviz.html">write_graphviz</a>
<LI><a href="read_graphviz.html">read_graphviz</a>
</OL>
<li>Graph Input/Output
<ol>
<li>AT&amp;T Graphviz: <a href="read_graphviz.html">read_graphviz</a>, <a href="./write-graphviz.html">write_graphviz</a></li>
<li>DIMACS Max-flow: <a href="read_dimacs.html">read_dimacs</a>, <a href="write_dimacs.html">write_dimacs</a></li>
</ol></li>
<LI>Auxiliary Concepts, Classes, and Functions
<OL>

104
doc/write_dimacs.html Normal file
View File

@ -0,0 +1,104 @@
<HTML>
<!--
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
-->
<Head>
<Title>Boost Graph Library: write_dimacs_max_flow</Title>
<BODY BGCOLOR="#ffffff" LINK="#0000ee" TEXT="#000000" VLINK="#551a8b"
ALINK="#ff0000">
<IMG SRC="../../../boost.png"
ALT="C++ Boost" width="277" height="86">
<BR Clear>
<H1><A NAME="sec:write_dimacs_max_flow">
<TT>write_dimacs</TT>
</H1>
<pre>
//outputs a graph with including edge_capacity properties to an std::ostream
template &lt; typename Graph, typename CapacityMap, typename IndexMap &gt;
void write_dimacs_max_flow(Graph& g,
CapacityMap capacity,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor& src,
typename graph_traits<Graph>::vertex_descriptor& sink,
std::ostream& out)
</pre>
<p>
This method writes a BGL graph object as an max-flow problem into an output stream in extended dimacs format (see <a href="http://www.avglab.com/andrew/CATS/maxflow_formats.htm"><TT>Goldbergs site</TT></a> for more information).
The output can be read in again using the <a href="../../../boost/graph/read_dimacs.hpp"><TT>boost/graph/read_dimacs.hpp</TT></a> method.
<H3>Where Defined</H3>
<P>
<a href="../../../boost/graph/write_dimacs.hpp"><TT>boost/graph/write_dimacs.hpp</TT></a>
<h3>Parameters</h3>
IN: <tt>Graph&amp; g</tt>
<blockquote>
A directed or undirected graph. The graph's type must be a model of
<a href="./VertexListGraph.html">VertexListGraph</a> and <a href="./EdgeListGraph.html">EdgeListGraph</a>, as num_vertices(Graph) and num_edges(Graph) is used inside. <a href="#1">[1]</a>
</blockquote>
IN: <tt>CapacityMap capacity</tt>
<blockquote>
A property map that models <a href="../../property_map/ReadablePropertyMap.html">Readable Property Map</a> whose key type is the edge descriptor of the graph and whose value type can be written to a stream. <br>
</blockquote>
IN: <tt>IndexMap epw</tt>
<blockquote>
A property map that models <a href="../../property_map/ReadablePropertyMap.html">Readable Property Map</a> whose key type is the vertex descriptor of the graph and whose value type can be written to a stream. <br>
</blockquote>
OUT: <tt>std::ostream&amp; out</tt>
<blockquote>
A standard <tt>std::ostream</tt> object.
</blockquote>
<H3>
Example
</H3>
A short <a href="../example/read_write_dimacs-eg.cpp">example</a> which uses read_dimacs and write_dimacs is located in the examples directory.
<h3>See Also</h3>
<a href="./read_dimacs.html"><tt>read_dimacs</tt></a>
<h3>Notes</h3>
<a name="1">[1]</a> As num_edges() and num_vertices() is used inside which returns values for the unfiltered graph (instead of the filtered), this method cannot be used with a <a href="./filtered_graph.html">filtered_graph</a>See <a href="./filtered_graph.html#2">filtered_graph Note [2]</a> for the reason.
</BODY>
</HTML>

112
example/kolmogorov-eg.cpp Normal file
View File

@ -0,0 +1,112 @@
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#include <iostream>
#include <string>
#include <boost/graph/kolmogorov_max_flow.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/read_dimacs.hpp>
#include <boost/graph/graph_utility.hpp>
// Use a DIMACS network flow file as stdin.
// kolmogorov-eg < max_flow.dat
//
// Sample output:
// c The total flow:
// s 13
//
// c flow values:
// f 0 6 3
// f 0 1 6
// f 0 2 4
// f 1 5 1
// f 1 0 0
// f 1 3 5
// f 2 4 4
// f 2 3 0
// f 2 0 0
// f 3 7 5
// f 3 2 0
// f 3 1 0
// f 4 5 0
// f 4 6 4
// f 5 4 0
// f 5 7 1
// f 6 7 7
// f 6 4 0
// f 7 6 0
// f 7 5 0
int
main()
{
using namespace boost;
typedef adjacency_list_traits < vecS, vecS, directedS > Traits;
typedef adjacency_list < vecS, vecS, directedS,
property < vertex_name_t, std::string,
property < vertex_index_t, long,
property < vertex_color_t, boost::default_color_type,
property < vertex_distance_t, long,
property < vertex_predecessor_t, Traits::edge_descriptor > > > > >,
property < edge_capacity_t, long,
property < edge_residual_capacity_t, long,
property < edge_reverse_t, Traits::edge_descriptor > > > > Graph;
Graph g;
property_map < Graph, edge_capacity_t >::type
capacity = get(edge_capacity, g);
property_map < Graph, edge_residual_capacity_t >::type
residual_capacity = get(edge_residual_capacity, g);
property_map < Graph, edge_reverse_t >::type rev = get(edge_reverse, g);
Traits::vertex_descriptor s, t;
read_dimacs_max_flow(g, capacity, rev, s, t);
std::vector<default_color_type> color(num_vertices(g));
std::vector<long> distance(num_vertices(g));
long flow = kolmogorov_max_flow(g ,s, t);
std::cout << "c The total flow:" << std::endl;
std::cout << "s " << flow << std::endl << std::endl;
std::cout << "c flow values:" << std::endl;
graph_traits < Graph >::vertex_iterator u_iter, u_end;
graph_traits < Graph >::out_edge_iterator ei, e_end;
for (tie(u_iter, u_end) = vertices(g); u_iter != u_end; ++u_iter)
for (tie(ei, e_end) = out_edges(*u_iter, g); ei != e_end; ++ei)
if (capacity[*ei] > 0)
std::cout << "f " << *u_iter << " " << target(*ei, g) << " "
<< (capacity[*ei] - residual_capacity[*ei]) << std::endl;
return EXIT_SUCCESS;
}

104
example/max_flow4.dat Normal file
View File

@ -0,0 +1,104 @@
c Random Network
p max 100 100
n 1 s
n 100 t
a 27 24 37
a 39 72 27
a 2 5 35
a 69 66 40
a 56 32 33
a 68 46 30
a 4 5 46
a 9 79 17
a 14 16 23
a 48 19 27
a 21 24 39
a 29 38 18
a 15 70 7
a 26 46 23
a 50 14 31
a 72 75 20
a 66 26 38
a 75 5 33
a 77 6 6
a 70 9 29
a 14 79 4
a 10 88 37
a 48 61 8
a 70 34 21
a 6 46 4
a 31 96 22
a 16 98 45
a 31 1 49
a 6 61 38
a 8 20 19
a 76 81 26
a 32 71 16
a 37 36 10
a 84 22 30
a 2 3 48
a 36 51 28
a 15 2 19
a 33 3 32
a 54 66 14
a 19 25 18
a 93 68 12
a 11 23 17
a 18 58 23
a 6 18 32
a 1 4 30
a 11 13 36
a 53 6 44
a 88 66 4
a 48 78 9
a 80 72 42
a 5 44 11
a 9 27 46
a 38 1 2
a 87 67 25
a 95 6 48
a 93 72 23
a 97 85 49
a 36 32 47
a 28 4 41
a 63 24 4
a 29 56 9
a 3 7 27
a 8 1 21
a 4 8 32
a 3 2 6
a 5 4 25
a 48 10 7
a 85 41 21
a 4 28 44
a 70 97 13
a 5 56 37
a 8 15 6
a 80 26 24
a 18 13 34
a 23 80 8
a 9 55 33
a 31 52 50
a 20 10 7
a 84 62 34
a 29 43 6
a 6 20 25
a 80 58 15
a 46 26 43
a 74 51 42
a 100 60 43
a 39 88 35
a 45 41 1
a 25 65 31
a 6 8 33
a 100 51 40
a 11 75 21
a 1 14 46
a 16 41 6
a 53 45 50
a 4 22 23
a 14 83 6
a 17 83 32
a 100 83 1
a 16 8 41
a 57 17 44

6004
example/max_flow5.dat Normal file

File diff suppressed because it is too large Load Diff

404
example/max_flow6.dat Normal file
View File

@ -0,0 +1,404 @@
c Random Network
p max 100 400
n 1 s
n 100 t
a 74 44 94
a 2 4 67
a 34 45 100
a 4 35 14
a 25 28 62
a 34 26 91
a 10 90 25
a 13 3 77
a 58 24 19
a 22 34 52
a 61 34 20
a 3 12 48
a 80 54 80
a 13 86 20
a 2 5 7
a 44 34 41
a 5 1 32
a 8 13 87
a 20 31 90
a 90 3 74
a 37 47 69
a 65 54 82
a 89 66 1
a 40 36 16
a 1 19 40
a 3 4 55
a 3 11 35
a 61 85 72
a 47 66 70
a 82 87 59
a 33 74 75
a 81 91 75
a 2 12 7
a 25 45 30
a 20 18 56
a 3 21 63
a 9 10 26
a 25 3 37
a 3 63 23
a 1 3 64
a 4 13 50
a 5 68 14
a 38 73 56
a 64 96 60
a 65 89 30
a 14 37 81
a 97 53 56
a 6 30 36
a 10 9 41
a 13 9 83
a 15 6 47
a 7 1 13
a 37 73 6
a 50 62 33
a 99 1 58
a 71 1 59
a 95 7 91
a 1 34 92
a 45 67 22
a 96 3 15
a 7 39 84
a 1 2 66
a 4 3 12
a 13 85 93
a 3 1 5
a 7 66 63
a 21 92 73
a 33 82 82
a 43 82 32
a 64 47 54
a 98 90 37
a 65 28 34
a 2 73 41
a 20 35 55
a 71 31 25
a 10 32 47
a 94 99 37
a 13 8 27
a 100 13 79
a 7 18 81
a 40 71 64
a 85 1 44
a 18 7 67
a 12 10 68
a 59 32 38
a 11 97 97
a 35 26 17
a 94 63 10
a 87 1 47
a 9 16 10
a 37 29 14
a 68 78 64
a 81 96 47
a 3 5 25
a 3 6 1
a 11 21 44
a 34 12 6
a 36 32 18
a 86 44 26
a 53 87 8
a 48 13 93
a 48 25 43
a 97 37 82
a 11 17 77
a 89 86 57
a 49 52 53
a 49 59 86
a 6 15 1
a 24 43 100
a 8 25 70
a 29 13 17
a 4 9 37
a 6 10 48
a 45 95 17
a 61 76 56
a 11 10 15
a 23 63 67
a 19 53 21
a 19 56 71
a 36 35 64
a 63 8 90
a 74 25 58
a 54 77 50
a 57 81 61
a 28 54 79
a 61 23 74
a 12 23 8
a 27 36 72
a 60 72 37
a 35 51 92
a 54 11 24
a 57 15 79
a 2 3 55
a 75 7 42
a 39 83 64
a 18 36 63
a 61 5 79
a 34 3 28
a 16 96 5
a 33 30 82
a 33 38 27
a 10 1 20
a 40 45 48
a 4 8 14
a 36 73 28
a 1 4 18
a 12 4 13
a 14 9 9
a 100 35 33
a 86 43 78
a 59 43 75
a 4 2 34
a 23 12 63
a 85 44 75
a 28 56 48
a 7 9 64
a 35 52 58
a 70 93 54
a 93 41 100
a 73 17 68
a 4 6 47
a 4 5 73
a 5 15 4
a 26 38 57
a 7 14 82
a 35 31 5
a 47 62 96
a 75 33 45
a 58 15 12
a 94 75 94
a 87 60 28
a 2 1 92
a 4 12 84
a 22 19 14
a 27 38 16
a 55 35 23
a 50 79 84
a 9 1 53
a 39 12 68
a 73 8 89
a 1 5 81
a 28 9 33
a 58 26 40
a 29 2 17
a 75 6 3
a 89 40 45
a 15 64 68
a 58 5 28
a 9 22 12
a 3 34 93
a 87 10 97
a 2 49 28
a 14 48 83
a 8 19 35
a 13 59 93
a 35 82 59
a 8 58 81
a 54 85 41
a 31 10 100
a 78 94 41
a 25 84 48
a 70 82 70
a 2 10 60
a 47 96 7
a 6 34 63
a 1 9 19
a 27 87 47
a 5 7 44
a 11 4 89
a 98 8 26
a 28 25 69
a 13 61 95
a 16 97 73
a 16 90 73
a 1 72 82
a 60 32 30
a 5 3 38
a 88 91 20
a 3 70 97
a 9 13 29
a 19 54 25
a 17 25 97
a 37 46 2
a 8 83 19
a 42 96 97
a 22 96 98
a 25 20 32
a 3 2 26
a 29 100 9
a 8 33 20
a 35 30 16
a 4 95 69
a 84 63 33
a 58 72 82
a 12 6 39
a 44 8 99
a 23 13 54
a 100 93 54
a 56 41 50
a 91 10 16
a 11 58 100
a 29 67 73
a 7 68 98
a 9 75 22
a 75 92 96
a 93 61 60
a 30 31 100
a 5 6 77
a 11 30 36
a 49 22 7
a 59 53 20
a 9 80 63
a 46 99 58
a 50 37 68
a 21 8 67
a 3 29 48
a 3 88 5
a 17 29 22
a 13 51 34
a 38 86 49
a 82 33 38
a 20 70 43
a 6 5 29
a 30 48 62
a 94 83 97
a 7 53 50
a 64 21 4
a 6 9 71
a 80 33 69
a 98 51 26
a 11 1 23
a 92 1 7
a 69 29 36
a 99 98 33
a 4 7 84
a 51 88 45
a 75 78 30
a 81 1 92
a 62 38 71
a 8 16 69
a 41 49 27
a 38 42 82
a 22 66 77
a 5 22 88
a 27 30 67
a 40 87 4
a 78 100 95
a 87 40 52
a 32 75 92
a 61 84 46
a 26 9 85
a 41 55 1
a 24 10 39
a 55 95 55
a 85 75 63
a 87 91 69
a 67 24 93
a 15 24 58
a 7 27 90
a 32 58 68
a 23 26 79
a 49 28 87
a 6 3 72
a 14 13 28
a 20 7 74
a 3 89 58
a 5 2 39
a 35 8 100
a 12 24 18
a 69 7 11
a 96 9 61
a 63 39 22
a 84 30 43
a 24 52 33
a 80 26 9
a 10 16 78
a 21 34 18
a 5 14 40
a 18 1 14
a 36 10 38
a 79 56 25
a 85 59 39
a 64 32 27
a 75 83 71
a 2 67 10
a 60 66 81
a 14 8 83
a 54 50 57
a 12 2 37
a 2 11 97
a 61 70 83
a 37 21 98
a 19 64 18
a 9 7 9
a 11 39 84
a 43 34 29
a 3 14 74
a 49 18 85
a 33 91 89
a 21 93 47
a 15 46 93
a 34 100 9
a 63 12 40
a 70 34 9
a 39 63 57
a 10 19 78
a 54 47 95
a 20 48 45
a 8 15 84
a 12 3 89
a 71 48 71
a 68 96 38
a 6 18 4
a 88 46 62
a 48 44 99
a 30 1 20
a 24 23 46
a 71 72 79
a 99 44 92
a 61 52 34
a 29 3 11
a 64 67 9
a 72 15 61
a 26 24 43
a 29 46 36
a 94 37 64
a 95 71 99
a 45 8 54
a 85 53 95
a 94 78 74
a 74 65 83
a 50 25 42
a 25 4 53
a 7 5 17
a 25 42 66
a 47 24 1
a 80 13 66
a 88 17 28
a 18 30 30
a 45 10 96
a 95 18 36
a 98 12 20
a 1 30 65
a 99 39 50
a 1 6 98
a 20 8 53
a 28 8 47
a 91 49 69
a 72 12 33
a 28 4 91
a 39 8 28
a 58 34 23
a 27 88 21
a 3 18 41
a 14 18 50
a 66 38 85
a 76 66 83
a 35 1 37
a 89 88 92
a 7 31 40

10004
example/max_flow7.dat Normal file

File diff suppressed because it is too large Load Diff

8004
example/max_flow8.dat Normal file

File diff suppressed because it is too large Load Diff

10004
example/max_flow9.dat Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#include <iostream>
#include <string>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/read_dimacs.hpp>
#include <boost/graph/write_dimacs.hpp>
/*************************************
*
* example which reads in a max-flow problem from std::cin, augments all paths from
* source->NODE->sink and writes the graph back to std::cout
*
**************************************/
template <typename EdgeCapacityMap>
struct zero_edge_capacity{
zero_edge_capacity() { }
zero_edge_capacity(EdgeCapacityMap cap_map):m_cap_map(cap_map){};
template <typename Edge>
bool operator() (const Edge& e) const {
return get(m_cap_map, e) == 0 ;
}
EdgeCapacityMap m_cap_map;
};
int main()
{
using namespace boost;
typedef adjacency_list_traits < vecS, vecS, directedS > Traits;
typedef adjacency_list < vecS, vecS, directedS,
no_property,
property < edge_capacity_t, long,
property < edge_reverse_t, Traits::edge_descriptor > > > Graph;
typedef graph_traits<Graph>::out_edge_iterator out_edge_iterator;
typedef graph_traits<Graph>::edge_descriptor edge_descriptor;
typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor;
Graph g;
typedef property_map < Graph, edge_capacity_t >::type tCapMap;
typedef tCapMap::value_type tCapMapValue;
typedef property_map < Graph, edge_reverse_t >::type tRevEdgeMap;
tCapMap capacity = get(edge_capacity, g);
tRevEdgeMap rev = get(edge_reverse, g);
vertex_descriptor s, t;
/*reading the graph from stdin*/
read_dimacs_max_flow(g, capacity, rev, s, t, std::cin);
/*process graph*/
tCapMapValue augmented_flow = 0;
//we take the source node and check for each outgoing edge e which has a target(p) if we can augment that path
out_edge_iterator oei,oe_end;
for(tie(oei, oe_end) = out_edges(s, g); oei != oe_end; ++oei){
edge_descriptor from_source = *oei;
vertex_descriptor v = target(from_source, g);
edge_descriptor to_sink;
bool is_there;
tie(to_sink, is_there) = edge(v, t, g);
if( is_there ){
if( get(capacity, to_sink) > get(capacity, from_source) ){
tCapMapValue to_augment = get(capacity, from_source);
capacity[from_source] = 0;
capacity[to_sink] -= to_augment;
augmented_flow += to_augment;
}else{
tCapMapValue to_augment = get(capacity, to_sink);
capacity[to_sink] = 0;
capacity[from_source] -= to_augment;
augmented_flow += to_augment;
}
}
}
//remove edges with zero capacity (most of them are the reverse edges)
zero_edge_capacity<tCapMap> filter(capacity);
remove_edge_if(filter, g);
/*write the graph back to stdout */
write_dimacs_max_flow(g, capacity, identity_property_map(),s, t, std::cout);
//print flow we augmented to std::cerr
std::cerr << "removed " << augmented_flow << " from SOURCE->NODE->SINK connects" <<std::endl;
return 0;
}

View File

@ -0,0 +1,807 @@
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_KOLMOGOROV_MAX_FLOW_HPP
#define BOOST_KOLMOGOROV_MAX_FLOW_HPP
#include <boost/config.hpp>
#include <cassert>
#include <vector>
#include <list>
#include <utility>
#include <iosfwd>
#include <algorithm> // for std::min and std::max
#include <boost/pending/queue.hpp>
#include <boost/limits.hpp>
#include <boost/property_map.hpp>
#include <boost/none_t.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/named_function_params.hpp>
namespace boost {
namespace detail {
template <class Graph,
class EdgeCapacityMap,
class ResidualCapacityEdgeMap,
class ReverseEdgeMap,
class PredecessorMap,
class ColorMap,
class DistanceMap,
class IndexMap>
class kolmogorov{
typedef typename property_traits<EdgeCapacityMap>::value_type tEdgeVal;
typedef graph_traits<Graph> tGraphTraits;
typedef typename tGraphTraits::vertex_iterator vertex_iterator;
typedef typename tGraphTraits::vertex_descriptor vertex_descriptor;
typedef typename tGraphTraits::edge_descriptor edge_descriptor;
typedef typename tGraphTraits::edge_iterator edge_iterator;
typedef typename tGraphTraits::out_edge_iterator out_edge_iterator;
typedef boost::queue<vertex_descriptor> tQueue; //queue of vertices, used in adoption-stage
typedef typename property_traits<ColorMap>::value_type tColorValue;
typedef color_traits<tColorValue> tColorTraits;
typedef typename property_traits<DistanceMap>::value_type tDistanceVal;
public:
kolmogorov(Graph& g,
EdgeCapacityMap cap,
ResidualCapacityEdgeMap res,
ReverseEdgeMap rev,
PredecessorMap pre,
ColorMap color,
DistanceMap dist,
IndexMap idx,
vertex_descriptor src,
vertex_descriptor sink):
m_g(g),
m_index_map(idx),
m_cap_map(cap),
m_res_cap_map(res),
m_rev_edge_map(rev),
m_pre_map(pre),
m_tree_map(color),
m_dist_map(dist),
m_source(src),
m_sink(sink),
m_active_nodes(),
m_in_active_list_vec(num_vertices(g), false),
m_in_active_list_map(make_iterator_property_map(m_in_active_list_vec.begin(), m_index_map)),
m_has_parent_vec(num_vertices(g), false),
m_has_parent_map(make_iterator_property_map(m_has_parent_vec.begin(), m_index_map)),
m_time_vec(num_vertices(g), 0),
m_time_map(make_iterator_property_map(m_time_vec.begin(), m_index_map)),
m_flow(0),
m_time(1),
m_last_grow_vertex(graph_traits<Graph>::null_vertex()){
// initialize the color-map with gray-values
vertex_iterator vi, v_end;
for(tie(vi, v_end) = vertices(m_g); vi != v_end; ++vi){
set_tree(*vi, tColorTraits::gray());
}
// Initialize flow to zero which means initializing
// the residual capacity equal to the capacity
edge_iterator ei, e_end;
for(tie(ei, e_end) = edges(m_g); ei != e_end; ++ei) {
m_res_cap_map[*ei] = m_cap_map[*ei];
assert(m_rev_edge_map[m_rev_edge_map[*ei]] == *ei); //check if the reverse edge map is build up properly
}
//init the search trees with the two terminals
set_tree(m_source, tColorTraits::white());
set_tree(m_sink, tColorTraits::black());
m_time_map[m_source] = 1;
m_time_map[m_sink] = 1;
}
~kolmogorov(){}
tEdgeVal max_flow(){
//augment direct paths from SOURCE->SINK and SOURCE->VERTEX->SINK
augment_direct_paths();
//start the main-loop
while(true){
bool path_found;
edge_descriptor connecting_edge;
tie(connecting_edge, path_found) = grow(); //find a path from source to sink
if(!path_found){
//we're finished, no more paths were found
break;
}
++m_time;
augment(connecting_edge); //augment that path
adopt(); //rebuild search tree structure
}
return m_flow;
}
//the complete class is protected, as we want access to members in derived test-class (see $(BOOST_ROOT)/libs/graph/test/kolmogorov_max_flow_test.cpp)
protected:
void augment_direct_paths(){
//in a first step, we augment all direct paths from source->NODE->sink
//and additionally paths from source->sink
//this improves especially graphcuts for segmentation, as most of the nodes have source/sink connects
//but shouldn't have an impact on other maxflow problems (this is done in grow() anyway)
out_edge_iterator ei, e_end;
for(tie(ei, e_end) = out_edges(m_source, m_g); ei != e_end; ++ei){
edge_descriptor from_source = *ei;
vertex_descriptor current_node = target(from_source, m_g);
if(current_node == m_sink){
tEdgeVal cap = m_res_cap_map[from_source];
m_res_cap_map[from_source] = 0;
m_flow += cap;
continue;
}
edge_descriptor to_sink;
bool is_there;
tie(to_sink, is_there) = edge(current_node, m_sink, m_g);
if(is_there){
tEdgeVal cap_from_source = m_res_cap_map[from_source];
tEdgeVal cap_to_sink = m_res_cap_map[to_sink];
if(cap_from_source > cap_to_sink){
set_tree(current_node, tColorTraits::white());
add_active_node(current_node);
set_edge_to_parent(current_node, from_source);
m_dist_map[current_node] = 1;
m_time_map[current_node] = 1;
//add stuff to flow and update residuals
//we dont need to update reverse_edges, as incoming/outgoing edges to/from source/sink don't count for max-flow
m_res_cap_map[from_source] -= cap_to_sink;
m_res_cap_map[to_sink] = 0;
m_flow += cap_to_sink;
} else if(cap_to_sink > 0){
set_tree(current_node, tColorTraits::black());
add_active_node(current_node);
set_edge_to_parent(current_node, to_sink);
m_dist_map[current_node] = 1;
m_time_map[current_node] = 1;
//add stuff to flow and update residuals
//we dont need to update reverse_edges, as incoming/outgoing edges to/from source/sink don't count for max-flow
m_res_cap_map[to_sink] -= cap_from_source;
m_res_cap_map[from_source] = 0;
m_flow += cap_from_source;
}
} else if(m_res_cap_map[from_source]){
//there is no sink connect, so we can't augment this path
//but to avoid adding m_source to the active nodes, we just activate this node and set the approciate things
set_tree(current_node, tColorTraits::white());
set_edge_to_parent(current_node, from_source);
m_dist_map[current_node] = 1;
m_time_map[current_node] = 1;
add_active_node(current_node);
}
}
for(tie(ei, e_end) = out_edges(m_sink, m_g); ei != e_end; ++ei){
edge_descriptor to_sink = m_rev_edge_map[*ei];
vertex_descriptor current_node = source(to_sink, m_g);
if(m_res_cap_map[to_sink]){
set_tree(current_node, tColorTraits::black());
set_edge_to_parent(current_node, to_sink);
m_dist_map[current_node] = 1;
m_time_map[current_node] = 1;
add_active_node(current_node);
}
}
}
/**
* returns a pair of an edge and a boolean. if the bool is true, the edge is a connection of a found path from s->t , read "the link" and
* source(returnVal, m_g) is the end of the path found in the source-tree
* target(returnVal, m_g) is the beginning of the path found in the sink-tree
*/
std::pair<edge_descriptor, bool> grow(){
assert(m_orphans.empty());
vertex_descriptor current_node;
while((current_node = get_next_active_node()) != graph_traits<Graph>::null_vertex()){ //if there is one
assert(get_tree(current_node) != tColorTraits::gray() && (has_parent(current_node) || current_node==m_source || current_node==m_sink));
if(get_tree(current_node) == tColorTraits::white()){
//source tree growing
out_edge_iterator ei, e_end;
if(current_node != m_last_grow_vertex){
m_last_grow_vertex = current_node;
tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g);
}
for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it){
edge_descriptor out_edge = *m_last_grow_edge_it;
if(m_res_cap_map[out_edge] > 0){ //check if we have capacity left on this edge
vertex_descriptor other_node = target(out_edge, m_g);
if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node
set_tree(other_node, tColorTraits::white()); //aquire other node to our search tree
set_edge_to_parent(other_node, out_edge); //set us as parent
m_dist_map[other_node] = m_dist_map[current_node] + 1; //and update the distance-heuristic
m_time_map[other_node] = m_time_map[current_node];
add_active_node(other_node);
} else if(get_tree(other_node) == tColorTraits::white()){
if(is_closer_to_terminal(current_node, other_node)){ //we do this to get shorter paths. check if we are nearer to the source as its parent is
set_edge_to_parent(other_node, out_edge);
m_dist_map[other_node] = m_dist_map[current_node] + 1;
m_time_map[other_node] = m_time_map[current_node];
}
} else{
assert(get_tree(other_node)==tColorTraits::black());
//kewl, found a path from one to the other search tree, return the connecting edge in src->sink dir
return std::make_pair(out_edge, true);
}
}
} //for all out-edges
} //source-tree-growing
else{
assert(get_tree(current_node) == tColorTraits::black());
out_edge_iterator ei, e_end;
if(current_node != m_last_grow_vertex){
m_last_grow_vertex = current_node;
tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g);
}
for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it){
edge_descriptor in_edge = m_rev_edge_map[*m_last_grow_edge_it];
if(m_res_cap_map[in_edge] > 0){ //check if there is capacity left
vertex_descriptor other_node = source(in_edge, m_g);
if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node
set_tree(other_node, tColorTraits::black()); //aquire that node to our search tree
set_edge_to_parent(other_node, in_edge); //set us as parent
add_active_node(other_node); //activate that node
m_dist_map[other_node] = m_dist_map[current_node] + 1; //set its distance
m_time_map[other_node] = m_time_map[current_node]; //and time
} else if(get_tree(other_node) == tColorTraits::black()){
if(is_closer_to_terminal(current_node, other_node)){
//we are closer to the sink than its parent is, so we "adopt" him
set_edge_to_parent(other_node, in_edge);
m_dist_map[other_node] = m_dist_map[current_node] + 1;
m_time_map[other_node] = m_time_map[current_node];
}
} else{
assert(get_tree(other_node)==tColorTraits::white());
//kewl, found a path from one to the other search tree, return the connecting edge in src->sink dir
return std::make_pair(in_edge, true);
}
}
} //for all out-edges
} //sink-tree growing
//all edges of that node are processed, and no more paths were found. so remove if from the front of the active queue
finish_node(current_node);
} //while active_nodes not empty
return std::make_pair(edge_descriptor(), false); //no active nodes anymore and no path found, we're done
}
/**
* augments path from s->t and updates residual graph
* source(e, m_g) is the end of the path found in the source-tree
* target(e, m_g) is the beginning of the path found in the sink-tree
* this phase generates orphans on satured edges, if the attached verts are from different search-trees
* orphans are ordered in distance to sink/source. first the farest from the source are front_inserted into the orphans list,
* and after that the sink-tree-orphans are front_inserted. when going to adoption stage the orphans are popped_front, and so we process the nearest
* verts to the terminals first
*/
void augment(edge_descriptor e){
assert(get_tree(target(e, m_g)) == tColorTraits::black());
assert(get_tree(source(e, m_g)) == tColorTraits::white());
assert(m_orphans.empty());
const tEdgeVal bottleneck = find_bottleneck(e);
//now we push the found flow through the path
//for each edge we saturate we have to look for the verts that belong to that edge, one of them becomes an orphans
//now process the connecting edge
m_res_cap_map[e] -= bottleneck;
assert(m_res_cap_map[e] >= 0);
m_res_cap_map[m_rev_edge_map[e]] += bottleneck;
//now we follow the path back to the source
vertex_descriptor current_node = source(e, m_g);
while(current_node != m_source){
edge_descriptor pred = get_edge_to_parent(current_node);
m_res_cap_map[pred] -= bottleneck;
assert(m_res_cap_map[pred] >= 0);
m_res_cap_map[m_rev_edge_map[pred]] += bottleneck;
if(m_res_cap_map[pred] == 0){
set_no_parent(current_node);
m_orphans.push_front(current_node);
}
current_node = source(pred, m_g);
}
//then go forward in the sink-tree
current_node = target(e, m_g);
while(current_node != m_sink){
edge_descriptor pred = get_edge_to_parent(current_node);
m_res_cap_map[pred] -= bottleneck;
assert(m_res_cap_map[pred] >= 0);
m_res_cap_map[m_rev_edge_map[pred]] += bottleneck;
if(m_res_cap_map[pred] == 0){
set_no_parent(current_node);
m_orphans.push_front(current_node);
}
current_node = target(pred, m_g);
}
//and add it to the max-flow
m_flow += bottleneck;
}
/**
* returns the bottleneck of a s->t path (end_of_path is last vertex in source-tree, begin_of_path is first vertex in sink-tree)
*/
inline tEdgeVal find_bottleneck(edge_descriptor e){
BOOST_USING_STD_MIN();
tEdgeVal minimum_cap = m_res_cap_map[e];
vertex_descriptor current_node = source(e, m_g);
//first go back in the source tree
while(current_node != m_source){
edge_descriptor pred = get_edge_to_parent(current_node);
minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, m_res_cap_map[pred]);
current_node = source(pred, m_g);
}
//then go forward in the sink-tree
current_node = target(e, m_g);
while(current_node != m_sink){
edge_descriptor pred = get_edge_to_parent(current_node);
minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, m_res_cap_map[pred]);
current_node = target(pred, m_g);
}
return minimum_cap;
}
/**
* rebuild search trees
* empty the queue of orphans, and find new parents for them or just drop them from the search trees
*/
void adopt(){
while(!m_orphans.empty() || !m_child_orphans.empty()){
vertex_descriptor current_node;
if(m_child_orphans.empty()){
//get the next orphan from the main-queue and remove it
current_node = m_orphans.front();
m_orphans.pop_front();
} else{
current_node = m_child_orphans.front();
m_child_orphans.pop();
}
if(get_tree(current_node) == tColorTraits::white()){
//we're in the source-tree
tDistanceVal min_distance = (std::numeric_limits<tDistanceVal>::max)();
edge_descriptor new_parent_edge;
out_edge_iterator ei, e_end;
for(tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
const edge_descriptor in_edge = m_rev_edge_map[*ei];
assert(target(in_edge, m_g) == current_node); //we should be the target of this edge
if(m_res_cap_map[in_edge] > 0){
vertex_descriptor other_node = source(in_edge, m_g);
if(get_tree(other_node) == tColorTraits::white() && has_source_connect(other_node)){
if(m_dist_map[other_node] < min_distance){
min_distance = m_dist_map[other_node];
new_parent_edge = in_edge;
}
}
}
}
if(min_distance != (std::numeric_limits<tDistanceVal>::max)()){
set_edge_to_parent(current_node, new_parent_edge);
m_dist_map[current_node] = min_distance + 1;
m_time_map[current_node] = m_time;
} else{
m_time_map[current_node] = 0;
for(tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
edge_descriptor in_edge = m_rev_edge_map[*ei];
vertex_descriptor other_node = source(in_edge, m_g);
if(get_tree(other_node) == tColorTraits::white() && has_parent(other_node)){
if(m_res_cap_map[in_edge] > 0){
add_active_node(other_node);
}
if(source(get_edge_to_parent(other_node), m_g) == current_node){
//we are the parent of that node
//it has to find a new parent, too
set_no_parent(other_node);
m_child_orphans.push(other_node);
}
}
}
set_tree(current_node, tColorTraits::gray());
} //no parent found
} //source-tree-adoption
else{
//now we should be in the sink-tree, check that...
assert(get_tree(current_node) == tColorTraits::black());
out_edge_iterator ei, e_end;
edge_descriptor new_parent_edge;
tDistanceVal min_distance = (std::numeric_limits<tDistanceVal>::max)();
for(tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
const edge_descriptor out_edge = *ei;
if(m_res_cap_map[out_edge] > 0){
const vertex_descriptor other_node = target(out_edge, m_g);
if(get_tree(other_node) == tColorTraits::black() && has_sink_connect(other_node))
if(m_dist_map[other_node] < min_distance){
min_distance = m_dist_map[other_node];
new_parent_edge = out_edge;
}
}
}
if(min_distance != (std::numeric_limits<tDistanceVal>::max)()){
set_edge_to_parent(current_node, new_parent_edge);
m_dist_map[current_node] = min_distance + 1;
m_time_map[current_node] = m_time;
} else{
m_time_map[current_node] = 0;
for(tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
const edge_descriptor out_edge = *ei;
const vertex_descriptor other_node = target(out_edge, m_g);
if(get_tree(other_node) == tColorTraits::black() && has_parent(other_node)){
if(m_res_cap_map[out_edge] > 0){
add_active_node(other_node);
}
if(target(get_edge_to_parent(other_node), m_g) == current_node){
//we were it's parent, so it has to find a new one, too
set_no_parent(other_node);
m_child_orphans.push(other_node);
}
}
}
set_tree(current_node, tColorTraits::gray());
} //no parent found
} //sink-tree adoption
} //while !orphans.empty()
} //adopt
/**
* return next active vertex if there is one, otherwise a null_vertex
*/
inline vertex_descriptor get_next_active_node(){
while(true){
if(m_active_nodes.empty())
return graph_traits<Graph>::null_vertex();
vertex_descriptor v = m_active_nodes.front();
if(!has_parent(v) && v != m_source && v != m_sink){ //if it has no parent, this node can't be active(if its not source or sink)
m_active_nodes.pop();
m_in_active_list_map[v] = false;
} else{
assert(get_tree(v) == tColorTraits::white() || get_tree(v) == tColorTraits::black());
return v;
}
}
}
/**
* adds v as an active vertex, but only if its not in the list already
*/
inline void add_active_node(vertex_descriptor v){
assert(get_tree(v) != tColorTraits::gray());
if(m_in_active_list_map[v]){
return;
} else{
m_in_active_list_map[v] = true;
m_active_nodes.push(v);
}
}
/**
* finish_node removes a node from the front of the active queue (its called in grow phase, if no more paths can be found using this node)
*/
inline void finish_node(vertex_descriptor v){
assert(m_active_nodes.front() == v);
m_active_nodes.pop();
m_in_active_list_map[v] = false;
m_last_grow_vertex = graph_traits<Graph>::null_vertex();
}
/**
* removes a vertex from the queue of active nodes (actually this does nothing,
* but checks if this node has no parent edge, as this is the criteria for beeing no more active)
*/
inline void remove_active_node(vertex_descriptor v){
assert(!has_parent(v));
}
/**
* returns the search tree of v; tColorValue::white() for source tree, black() for sink tree, gray() for no tree
*/
inline tColorValue get_tree(vertex_descriptor v) const {
return m_tree_map[v];
}
/**
* sets search tree of v; tColorValue::white() for source tree, black() for sink tree, gray() for no tree
*/
inline void set_tree(vertex_descriptor v, tColorValue t){
m_tree_map[v] = t;
}
/**
* returns edge to parent vertex of v;
*/
inline edge_descriptor get_edge_to_parent(vertex_descriptor v) const{
return m_pre_map[v];
}
/**
* returns true if the edge stored in m_pre_map[v] is a valid entry
*/
inline bool has_parent(vertex_descriptor v) const{
return m_has_parent_map[v];
}
/**
* sets edge to parent vertex of v;
*/
inline void set_edge_to_parent(vertex_descriptor v, edge_descriptor f_edge_to_parent){
assert(m_res_cap_map[f_edge_to_parent] > 0);
m_pre_map[v] = f_edge_to_parent;
m_has_parent_map[v] = true;
}
/**
* removes the edge to parent of v (this is done by invalidating the entry an additional map)
*/
inline void set_no_parent(vertex_descriptor v){
m_has_parent_map[v] = false;
}
/**
* checks if vertex v has a connect to the sink-vertex (@var m_sink)
* @param v the vertex which is checked
* @return true if a path to the sink was found, false if not
*/
inline bool has_sink_connect(vertex_descriptor v){
tDistanceVal current_distance = 0;
vertex_descriptor current_vertex = v;
while(true){
if(m_time_map[current_vertex] == m_time){
//we found a node which was already checked this round. use it for distance calculations
current_distance += m_dist_map[current_vertex];
break;
}
if(current_vertex == m_sink){
m_time_map[m_sink] = m_time;
break;
}
if(has_parent(current_vertex)){
//it has a parent, so get it
current_vertex = target(get_edge_to_parent(current_vertex), m_g);
++current_distance;
} else{
//no path found
return false;
}
}
current_vertex=v;
while(m_time_map[current_vertex] != m_time){
m_dist_map[current_vertex] = current_distance--;
m_time_map[current_vertex] = m_time;
current_vertex = target(get_edge_to_parent(current_vertex), m_g);
}
return true;
}
/**
* checks if vertex v has a connect to the source-vertex (@var m_source)
* @param v the vertex which is checked
* @return true if a path to the source was found, false if not
*/
inline bool has_source_connect(vertex_descriptor v){
tDistanceVal current_distance = 0;
vertex_descriptor current_vertex = v;
while(true){
if(m_time_map[current_vertex] == m_time){
//we found a node which was already checked this round. use it for distance calculations
current_distance += m_dist_map[current_vertex];
break;
}
if(current_vertex == m_source){
m_time_map[m_source] = m_time;
break;
}
if(has_parent(current_vertex)){
//it has a parent, so get it
current_vertex = source(get_edge_to_parent(current_vertex), m_g);
++current_distance;
} else{
//no path found
return false;
}
}
current_vertex=v;
while(m_time_map[current_vertex] != m_time){
m_dist_map[current_vertex] = current_distance-- ;
m_time_map[current_vertex] = m_time;
current_vertex = source(get_edge_to_parent(current_vertex), m_g);
}
return true;
}
/**
* returns true, if p is closer to a terminal than q
*/
inline bool is_closer_to_terminal(vertex_descriptor p, vertex_descriptor q){
//checks the timestamps first, to build no cycles, and after that the real distance
return (m_time_map[q] <= m_time_map[p] && m_dist_map[q] > m_dist_map[p]+1);
}
////////
// member vars
////////
Graph& m_g;
IndexMap m_index_map;
EdgeCapacityMap m_cap_map;
ResidualCapacityEdgeMap m_res_cap_map;
ReverseEdgeMap m_rev_edge_map;
PredecessorMap m_pre_map; //stores paths found in the growth stage
ColorMap m_tree_map; //maps each vertex into one of the two search tree or none (gray())
DistanceMap m_dist_map; //stores distance to source/sink nodes
vertex_descriptor m_source;
vertex_descriptor m_sink;
tQueue m_active_nodes;
std::vector<bool> m_in_active_list_vec;
iterator_property_map<std::vector<bool>::iterator, IndexMap> m_in_active_list_map;
std::list<vertex_descriptor> m_orphans;
tQueue m_child_orphans; // we use a second queuqe for child orphans, as they are FIFO processed
std::vector<bool> m_has_parent_vec;
iterator_property_map<std::vector<bool>::iterator, IndexMap> m_has_parent_map;
std::vector<long> m_time_vec; //timestamp of each node, used for sink/source-path calculations
iterator_property_map<std::vector<long>::iterator, IndexMap> m_time_map;
tEdgeVal m_flow;
long m_time;
vertex_descriptor m_last_grow_vertex;
out_edge_iterator m_last_grow_edge_it;
out_edge_iterator m_last_grow_edge_end;
};
} //namespace detail
/**
* non-named-parameter version, given everything
* this is the catch all version
*/
template <class Graph, class CapacityEdgeMap, class ResidualCapacityEdgeMap, class ReverseEdgeMap,
class PredecessorMap, class ColorMap, class DistanceMap, class IndexMap>
typename property_traits<CapacityEdgeMap>::value_type
kolmogorov_max_flow
(Graph& g,
CapacityEdgeMap cap,
ResidualCapacityEdgeMap res_cap,
ReverseEdgeMap rev_map,
PredecessorMap pre_map,
ColorMap color,
DistanceMap dist,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink
)
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
//as this method is the last one before we instantiate the solver, we do the concept checks here
function_requires<VertexListGraphConcept<Graph> >(); //to have vertices(), num_vertices(),
function_requires<EdgeListGraphConcept<Graph> >(); //to have edges()
function_requires<IncidenceGraphConcept<Graph> >(); //to have source(), target() and out_edges()
function_requires<LvaluePropertyMapConcept<CapacityEdgeMap, edge_descriptor> >(); //read flow-values from edges
function_requires<Mutable_LvaluePropertyMapConcept<ResidualCapacityEdgeMap, edge_descriptor> >(); //write flow-values to residuals
function_requires<LvaluePropertyMapConcept<ReverseEdgeMap, edge_descriptor> >(); //read out reverse edges
function_requires<Mutable_LvaluePropertyMapConcept<PredecessorMap, vertex_descriptor> >(); //store predecessor there
function_requires<Mutable_LvaluePropertyMapConcept<ColorMap, vertex_descriptor> >(); //write corresponding tree
function_requires<Mutable_LvaluePropertyMapConcept<DistanceMap, vertex_descriptor> >(); //write distance to source/sink
function_requires<ReadablePropertyMapConcept<IndexMap, vertex_descriptor> >(); //get index 0...|V|-1
assert(num_vertices(g) >= 2 && src != sink);
detail::kolmogorov<Graph, CapacityEdgeMap, ResidualCapacityEdgeMap, ReverseEdgeMap, PredecessorMap, ColorMap, DistanceMap, IndexMap>
algo(g, cap, res_cap, rev_map, pre_map, color, dist, idx, src, sink);
return algo.max_flow();
}
/**
* non-named-parameter version, given: capacity, residucal_capacity, reverse_edges, and an index map.
*/
template <class Graph, class CapacityEdgeMap, class ResidualCapacityEdgeMap, class ReverseEdgeMap, class IndexMap>
typename property_traits<CapacityEdgeMap>::value_type
kolmogorov_max_flow
(Graph& g,
CapacityEdgeMap cap,
ResidualCapacityEdgeMap res_cap,
ReverseEdgeMap rev,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink)
{
typename graph_traits<Graph>::vertices_size_type n_verts = num_vertices(g);
std::vector<typename graph_traits<Graph>::edge_descriptor> predecessor_vec(n_verts);
std::vector<default_color_type> color_vec(n_verts);
std::vector<typename graph_traits<Graph>::vertices_size_type> distance_vec(n_verts);
return kolmogorov_max_flow
(g, cap, res_cap, rev,
make_iterator_property_map(predecessor_vec.begin(), idx),
make_iterator_property_map(color_vec.begin(), idx),
make_iterator_property_map(distance_vec.begin(), idx),
idx, src, sink);
}
/**
* non-named-parameter version, some given: capacity, residual_capacity, reverse_edges, color_map and an index map.
* Use this if you are interested in the minimum cut, as the color map provides that info
*/
template <class Graph, class CapacityEdgeMap, class ResidualCapacityEdgeMap, class ReverseEdgeMap, class ColorMap, class IndexMap>
typename property_traits<CapacityEdgeMap>::value_type
kolmogorov_max_flow
(Graph& g,
CapacityEdgeMap cap,
ResidualCapacityEdgeMap res_cap,
ReverseEdgeMap rev,
ColorMap color,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink)
{
typename graph_traits<Graph>::vertices_size_type n_verts = num_vertices(g);
std::vector<typename graph_traits<Graph>::edge_descriptor> predecessor_vec(n_verts);
std::vector<typename graph_traits<Graph>::vertices_size_type> distance_vec(n_verts);
return kolmogorov_max_flow
(g, cap, res_cap, rev,
make_iterator_property_map(predecessor_vec.begin(), idx),
color,
make_iterator_property_map(distance_vec.begin(), idx),
idx, src, sink);
}
/**
* named-parameter version, some given
*/
template <class Graph, class P, class T, class R>
typename property_traits<typename property_map<Graph, edge_capacity_t>::const_type>::value_type
kolmogorov_max_flow
(Graph& g,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink,
const bgl_named_params<P, T, R>& params)
{
return kolmogorov_max_flow(g,
choose_const_pmap(get_param(params, edge_capacity), g, edge_capacity),
choose_pmap(get_param(params, edge_residual_capacity), g, edge_residual_capacity),
choose_const_pmap(get_param(params, edge_reverse), g, edge_reverse),
choose_pmap(get_param(params, vertex_predecessor), g, vertex_predecessor),
choose_pmap(get_param(params, vertex_color), g, vertex_color),
choose_pmap(get_param(params, vertex_distance), g, vertex_distance),
choose_const_pmap(get_param(params, vertex_index), g, vertex_index),
src, sink);
}
/**
* named-parameter version, none given
*/
template <class Graph>
typename property_traits<typename property_map<Graph, edge_capacity_t>::const_type>::value_type
kolmogorov_max_flow
(Graph& g,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink)
{
bgl_named_params<int, buffer_param_t> params(0); // bogus empty param
return kolmogorov_max_flow(g, src, sink, params);
}
} // namespace boost
#endif // BOOST_KOLMOGOROV_MAX_FLOW_HPP

View File

@ -9,15 +9,13 @@
/*
Reads maximal flow problem in extended DIMACS format.
Reads from stdin.
This works, but could use some polishing.
*/
/* ----------------------------------------------------------------- */
#include <vector>
#include <istream>
#include <stdio.h>
namespace boost {
@ -27,7 +25,8 @@ int read_dimacs_max_flow(Graph& g,
CapacityMap capacity,
ReverseEdgeMap reverse_edge,
typename graph_traits<Graph>::vertex_descriptor& src,
typename graph_traits<Graph>::vertex_descriptor& sink)
typename graph_traits<Graph>::vertex_descriptor& sink,
std::istream& in=std::cin)
{
// const int MAXLINE = 100; /* max line length in the input file */
const int ARC_FIELDS = 3; /* no of fields in arc line */
@ -114,7 +113,7 @@ int read_dimacs_max_flow(Graph& g,
- does service functions
*/
while (std::getline(std::cin, in_line)) {
while (std::getline(in, in_line)) {
++no_lines;
switch (in_line[0]) {

View File

@ -0,0 +1,72 @@
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/*
Writes maximal flow problem in extended DIMACS format to an OutputIterator
Vertex indices are read from an IndexMap and shiftet by 1.
so their new range is [1..num_vertices(g)]
*/
/* ----------------------------------------------------------------- */
#include <vector>
#include <string>
#include <ostream>
namespace boost {
template <class Graph, class CapacityMap, class IndexMap>
void write_dimacs_max_flow(const Graph& g,
CapacityMap capacity,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink,
std::ostream& out)
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
out << "c DIMACS max-flow file generated from boost::write_dimacs_max_flow" << std::endl;
out << "p max " << num_vertices(g) << " " << num_edges(g) << std::endl; //print problem description "max" and number of verts and edges
out << "n " << get(idx, src) + 1 << " s" << std::endl;; //say which one is source
out << "n " << get(idx, sink) + 1 << " t" << std::endl; //say which one is sink
//output the edges
edge_iterator ei, e_end;
for(tie(ei,e_end) = edges(g); ei!=e_end; ++ei){
out << "a " << idx[ source(*ei, g) ] + 1 << " " << idx[ target(*ei, g) ] + 1 << " " << get(capacity,*ei) << std::endl;
}
}
} // namespace boost

View File

@ -93,6 +93,9 @@ test-suite graph_test :
[ run matching_test.cpp ]
[ run max_flow_test.cpp ]
[ run kolmogorov_max_flow_test.cpp ]
;
# Run SDB tests only when -sSDB= is set.

View File

@ -0,0 +1,434 @@
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <vector>
#include <iterator>
#include <iostream>
#include <algorithm>
#include <fstream>
#include <boost/test/minimal.hpp>
#include <boost/graph/kolmogorov_max_flow.hpp>
//boost utilities we use
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adjacency_matrix.hpp>
#include <boost/graph/random.hpp>
#include <boost/property_map.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/lexical_cast.hpp>
using namespace boost;
template <typename Graph, typename CapacityMap, typename ReverseEdgeMap>
std::pair< typename graph_traits<Graph>::vertex_descriptor,typename graph_traits<Graph>::vertex_descriptor>
fill_random_max_flow_graph(Graph& g, CapacityMap cap, ReverseEdgeMap rev, typename graph_traits<Graph>::vertices_size_type n_verts,
typename graph_traits<Graph>::edges_size_type n_edges, std::size_t seed)
{
typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
const int cap_low = 1;
const int cap_high = 1000;
//init random numer generator
minstd_rand gen(seed);
//generate graph
generate_random_graph(g, n_verts, n_edges, gen);
//init an uniform distribution int generator
typedef variate_generator<minstd_rand, uniform_int<int> > tIntGen;
tIntGen int_gen(gen, uniform_int<int>(cap_low, cap_high));
//randomize edge-capacities
//randomize_property<edge_capacity, Graph, tIntGen> (g,int_gen); //we cannot use this, as we have no idea how properties are stored, right?
typename graph_traits<Graph>::edge_iterator ei, e_end;
for(tie(ei,e_end) = edges(g); ei != e_end; ++ei)
cap[*ei] = int_gen();
//get source and sink node
vertex_descriptor s = random_vertex(g, gen);
vertex_descriptor t = graph_traits<Graph>::null_vertex();
while(t == graph_traits<Graph>::null_vertex() || t == s)
t = random_vertex(g, gen);
//add reverse edges (ugly... how to do better?!)
std::list<edge_descriptor> edges_copy;
tie(ei, e_end) = edges(g);
std::copy(ei, e_end, std::back_insert_iterator< std::list<edge_descriptor> >(edges_copy));
while(!edges_copy.empty()){
edge_descriptor old_edge = edges_copy.front();
edges_copy.pop_front();
vertex_descriptor source_vertex = target(old_edge, g);
vertex_descriptor target_vertex = source(old_edge, g);
bool inserted;
edge_descriptor new_edge;
tie(new_edge,inserted) = add_edge(source_vertex, target_vertex, g);
assert(inserted);
rev[old_edge] = new_edge;
rev[new_edge] = old_edge ;
cap[new_edge] = 0;
}
return std::make_pair(s,t);
}
long test_adjacency_list_vecS(int n_verts, int n_edges, std::size_t seed){
typedef adjacency_list_traits<vecS, vecS, directedS> tVectorTraits;
typedef adjacency_list<vecS, vecS, directedS,
property<vertex_index_t, long,
property<vertex_predecessor_t, tVectorTraits::edge_descriptor,
property<vertex_color_t, boost::default_color_type,
property<vertex_distance_t, long> > > >,
property<edge_capacity_t, long,
property<edge_residual_capacity_t, long,
property<edge_reverse_t, tVectorTraits::edge_descriptor > > > > tVectorGraph;
tVectorGraph g;
graph_traits<tVectorGraph>::vertex_descriptor src,sink;
tie(src,sink) = fill_random_max_flow_graph(g, get(edge_capacity,g), get(edge_reverse, g), n_verts, n_edges, seed);
return kolmogorov_max_flow(g, get(edge_capacity, g),
get(edge_residual_capacity, g),
get(edge_reverse, g),
get(vertex_predecessor, g),
get(vertex_color, g),
get(vertex_distance, g),
get(vertex_index, g),
src, sink);
}
long test_adjacency_list_listS(int n_verts, int n_edges, std::size_t seed){
typedef adjacency_list_traits<listS, listS, directedS> tListTraits;
typedef adjacency_list<listS, listS, directedS,
property<vertex_index_t, long,
property<vertex_predecessor_t, tListTraits::edge_descriptor,
property<vertex_color_t, boost::default_color_type,
property<vertex_distance_t, long> > > >,
property<edge_capacity_t, long,
property<edge_residual_capacity_t, long,
property<edge_reverse_t, tListTraits::edge_descriptor > > > > tListGraph;
tListGraph g;
graph_traits<tListGraph>::vertex_descriptor src,sink;
tie(src,sink) = fill_random_max_flow_graph(g, get(edge_capacity,g), get(edge_reverse, g), n_verts, n_edges, seed);
//initialize vertex indices
graph_traits<tListGraph>::vertex_iterator vi,v_end;
graph_traits<tListGraph>::vertices_size_type index = 0;
for(tie(vi, v_end) = vertices(g); vi != v_end; ++vi){
put(vertex_index, g, *vi, index++);
}
return kolmogorov_max_flow(g, get(edge_capacity, g),
get(edge_residual_capacity, g),
get(edge_reverse, g),
get(vertex_predecessor, g),
get(vertex_color, g),
get(vertex_distance, g),
get(vertex_index, g),
src, sink);
}
template<typename EdgeDescriptor>
struct Node{
boost::default_color_type vertex_color;
long vertex_distance;
EdgeDescriptor vertex_predecessor;
};
template<typename EdgeDescriptor>
struct Link{
long edge_capacity;
long edge_residual_capacity;
EdgeDescriptor edge_reverse;
};
long test_bundled_properties(int n_verts, int n_edges, std::size_t seed){
typedef adjacency_list_traits<vecS, vecS, directedS> tTraits;
typedef Node<tTraits::edge_descriptor> tVertex;
typedef Link<tTraits::edge_descriptor> tEdge;
typedef adjacency_list<vecS, vecS, directedS, tVertex, tEdge> tBundleGraph;
tBundleGraph g;
graph_traits<tBundleGraph>::vertex_descriptor src,sink;
tie(src,sink) = fill_random_max_flow_graph(g, get(&tEdge::edge_capacity,g), get(&tEdge::edge_reverse, g), n_verts, n_edges, seed);
return kolmogorov_max_flow(g, get(&tEdge::edge_capacity, g),
get(&tEdge::edge_residual_capacity, g),
get(&tEdge::edge_reverse, g),
get(&tVertex::vertex_predecessor, g),
get(&tVertex::vertex_color, g),
get(&tVertex::vertex_distance, g),
get(vertex_index, g),
src, sink);
}
long test_overloads(int n_verts, int n_edges, std::size_t seed){
typedef adjacency_list_traits<vecS, vecS, directedS> tTraits;
typedef property <edge_capacity_t, long,
property<edge_residual_capacity_t, long,
property<edge_reverse_t, tTraits::edge_descriptor> > >tEdgeProperty;
typedef adjacency_list<vecS, vecS, directedS, no_property, tEdgeProperty> tGraph;
tGraph g;
graph_traits<tGraph>::vertex_descriptor src,sink;
tie(src,sink) = fill_random_max_flow_graph(g, get(edge_capacity,g), get(edge_reverse, g), n_verts, n_edges, seed);
std::vector<graph_traits<tGraph>::edge_descriptor> predecessor_vec(n_verts);
std::vector<default_color_type> color_vec(n_verts);
std::vector<graph_traits<tGraph>::vertices_size_type> distance_vec(n_verts);
long flow_overload_1 = kolmogorov_max_flow(g, get(edge_capacity,g), get(edge_residual_capacity,g), get(edge_reverse,g), get(vertex_index,g), src, sink);
long flow_overload_2 = kolmogorov_max_flow(g, get(edge_capacity,g), get(edge_residual_capacity,g), get(edge_reverse,g),
&(color_vec[0]), get(vertex_index,g), src, sink);
BOOST_CHECK(flow_overload_1 == flow_overload_2);
return flow_overload_1;
}
template <class Graph, class EdgeCapacityMap, class ResidualCapacityEdgeMap, class ReverseEdgeMap, class PredecessorMap, class ColorMap,
class DistanceMap, class IndexMap>
class kolmogorov_test:public detail::kolmogorov<Graph,EdgeCapacityMap,ResidualCapacityEdgeMap,ReverseEdgeMap,PredecessorMap,ColorMap,DistanceMap,IndexMap>
{
typedef typename graph_traits<Graph>::edge_descriptor tEdge;
typedef typename graph_traits<Graph>::vertex_descriptor tVertex;
typedef typename property_traits< typename property_map<Graph, edge_capacity_t>::const_type>::value_type tEdgeVal;
typedef typename graph_traits<Graph>::vertex_iterator tVertexIterator;
typedef typename graph_traits<Graph>::out_edge_iterator tOutEdgeIterator;
typedef typename property_traits<ColorMap>::value_type tColorValue;
typedef color_traits<tColorValue> tColorTraits;
typedef typename property_traits<DistanceMap>::value_type tDistanceVal;
typedef typename detail::kolmogorov<Graph,EdgeCapacityMap,ResidualCapacityEdgeMap,ReverseEdgeMap,PredecessorMap,ColorMap,DistanceMap,IndexMap> tSuper;
public:
kolmogorov_test(Graph& g, typename graph_traits<Graph>::vertex_descriptor src, typename graph_traits<Graph>::vertex_descriptor sink):
detail::kolmogorov<Graph,EdgeCapacityMap,ResidualCapacityEdgeMap,ReverseEdgeMap,PredecessorMap,ColorMap,DistanceMap,IndexMap>
(g, get(edge_capacity,g), get(edge_residual_capacity,g), get(edge_reverse, g), get(vertex_predecessor, g), get(vertex_color, g),
get(vertex_distance, g), get(vertex_index, g), src, sink){
}
void invariant_four(tVertex v) const{
//passive nodes in S or T
if(v == tSuper::m_source || v == tSuper::m_sink)
return;
typename std::list<tVertex>::const_iterator it = find(tSuper::m_orphans.begin(), tSuper::m_orphans.end(), v);
// a node is active, if its in the active_list AND (is has_a_parent, or its already in the orphans_list or its the sink, or its the source)
bool is_active = (tSuper::m_in_active_list_map[v] && (has_parent(v) || it != tSuper::m_orphans.end() ));
if(get_tree(v) != tColorTraits::gray() && !is_active){
typename graph_traits<Graph>::out_edge_iterator ei,e_end;
for(tie(ei, e_end) = out_edges(v, tSuper::m_g); ei != e_end; ++ei){
const tVertex& other_node = target(*ei, tSuper::m_g);
if(get_tree(other_node) != get_tree(v)){
if(get_tree(v) == tColorTraits::white())
BOOST_CHECK(tSuper::m_res_cap_map[*ei] == 0);
else
BOOST_CHECK(tSuper::m_res_cap_map[tSuper::m_rev_edge_map[*ei]] == 0);
}
}
}
}
void invariant_five(const tVertex& v) const{
BOOST_CHECK(get_tree(v) != tColorTraits::gray() || tSuper::m_time_map[v] <= tSuper::m_time);
}
void invariant_six(const tVertex& v) const{
if(get_tree(v) == tColorTraits::gray() || tSuper::m_time_map[v] != tSuper::m_time)
return;
else{
tVertex current_node = v;
tDistanceVal distance = 0;
tColorValue color = get_tree(v);
tVertex terminal = (color == tColorTraits::white()) ? tSuper::m_source : tSuper::m_sink;
while(current_node != terminal){
BOOST_CHECK(tSuper::has_parent(current_node));
tEdge e = get_edge_to_parent(current_node);
++distance;
current_node = (color == tColorTraits::white())? source(e, tSuper::m_g) : target(e, tSuper::m_g);
if(distance > tSuper::m_dist_map[v])
break;
}
BOOST_CHECK(distance == tSuper::m_dist_map[v]);
}
}
void invariant_seven(const tVertex& v) const{
if(get_tree(v) == tColorTraits::gray())
return;
else{
tColorValue color = get_tree(v);
long time = tSuper::m_time_map[v];
tVertex current_node = v;
while(tSuper::has_parent(current_node)){
tEdge e = get_edge_to_parent(current_node);
current_node = (color == tColorTraits::white()) ? source(e, tSuper::m_g) : target(e, tSuper::m_g);
BOOST_CHECK(tSuper::m_time_map[current_node] >= time);
}
}
}//invariant_seven
void invariant_eight(const tVertex& v) const{
if(get_tree(v) == tColorTraits::gray())
return;
else{
tColorValue color = get_tree(v);
long time = tSuper::m_time_map[v];
tDistanceVal distance = tSuper::m_dist_map[v];
tVertex current_node = v;
while(tSuper::has_parent(current_node)){
tEdge e = get_edge_to_parent(current_node);
current_node = (color == tColorTraits::white()) ? source(e, tSuper::m_g) : target(e, tSuper::m_g);
if(tSuper::m_time_map[current_node] == time)
BOOST_CHECK(tSuper::m_dist_map[current_node] < distance);
}
}
}//invariant_eight
void check_invariants(){
tVertexIterator vi, v_end;
for(tie(vi, v_end) = vertices(tSuper::m_g); vi != v_end; ++vi){
invariant_four(*vi);
invariant_five(*vi);
invariant_six(*vi);
invariant_seven(*vi);
invariant_eight(*vi);
}
}
tEdgeVal test(){
this->add_active_node(this->m_sink);
this->augment_direct_paths();
check_invariants();
//start the main-loop
while(true){
bool path_found;
tEdge connecting_edge;
tie(connecting_edge, path_found) = this->grow(); //find a path from source to sink
if(!path_found){
//we're finished, no more paths were found
break;
}
check_invariants();
this->m_time++;
this->augment(connecting_edge); //augment that path
check_invariants();
this->adopt(); //rebuild search tree structure
check_invariants();
}
//check if flow is the sum of outgoing edges of src
tOutEdgeIterator ei, e_end;
tEdgeVal src_sum = 0;
for(tie(ei, e_end) = out_edges(this->m_source, this->m_g); ei != e_end; ++ei){
src_sum += this->m_cap_map[*ei] - this->m_res_cap_map[*ei];
}
BOOST_CHECK(this->m_flow == src_sum);
//check if flow is the sum of ingoing edges of sink
tEdgeVal sink_sum = 0;
for(tie(ei, e_end) = out_edges(this->m_sink, this->m_g); ei != e_end; ++ei){
tEdge in_edge = this->m_rev_edge_map[*ei];
sink_sum += this->m_cap_map[in_edge] - this->m_res_cap_map[in_edge];
}
BOOST_CHECK(this->m_flow == sink_sum);
return this->m_flow;
}
};
long test_algorithms_invariant(int n_verts, int n_edges, std::size_t seed)
{
typedef adjacency_list_traits<vecS, vecS, directedS> tVectorTraits;
typedef adjacency_list<vecS, vecS, directedS,
property<vertex_index_t, long,
property<vertex_predecessor_t, tVectorTraits::edge_descriptor,
property<vertex_color_t, default_color_type,
property<vertex_distance_t, long> > > >,
property<edge_capacity_t, long,
property<edge_residual_capacity_t, long,
property<edge_reverse_t, tVectorTraits::edge_descriptor > > > > tVectorGraph;
tVectorGraph g;
graph_traits<tVectorGraph>::vertex_descriptor src, sink;
tie(src,sink) = fill_random_max_flow_graph(g, get(edge_capacity,g), get(edge_reverse, g), n_verts, n_edges, seed);
typedef property_map<tVectorGraph, edge_capacity_t>::type tEdgeCapMap;
typedef property_map<tVectorGraph, edge_residual_capacity_t>::type tEdgeResCapMap;
typedef property_map<tVectorGraph, edge_reverse_t>::type tRevEdgeMap;
typedef property_map<tVectorGraph, vertex_predecessor_t>::type tVertexPredMap;
typedef property_map<tVectorGraph, vertex_color_t>::type tVertexColorMap;
typedef property_map<tVectorGraph, vertex_distance_t>::type tDistanceMap;
typedef property_map<tVectorGraph, vertex_index_t>::type tIndexMap;
typedef kolmogorov_test<tVectorGraph, tEdgeCapMap, tEdgeResCapMap, tRevEdgeMap, tVertexPredMap, tVertexColorMap, tDistanceMap, tIndexMap> tKolmo;
tKolmo instance(g, src, sink);
return instance.test();
}
int test_main(int argc, char* argv[])
{
int n_verts = 10;
int n_edges = 500;
std::size_t seed = 1;
if (argc > 1) n_verts = lexical_cast<int>(argv[1]);
if (argc > 2) n_edges = lexical_cast<int>(argv[2]);
if (argc > 3) seed = lexical_cast<std::size_t>(argv[3]);
//we need at least 2 vertices to create src and sink in random graphs
//this case is also caught in kolmogorov_max_flow
if (n_verts<2)
n_verts = 2;
/*
* below are checks for different calls to kolmogorov_max_flow and different graph-types
*/
//checks support of vecS storage
long flow_vecS = test_adjacency_list_vecS(n_verts, n_edges, seed);
std::cout << "vecS flow: " << flow_vecS << std::endl;
//checks support of listS storage (especially problems with vertex indices)
long flow_listS = test_adjacency_list_listS(n_verts, n_edges, seed);
std::cout << "listS flow: " << flow_listS << std::endl;
BOOST_CHECK(flow_vecS == flow_listS);
//checks bundled properties
long flow_bundles = test_bundled_properties(n_verts, n_edges, seed);
std::cout << "bundles flow: " << flow_bundles << std::endl;
BOOST_CHECK(flow_listS == flow_bundles);
//checks overloads
long flow_overloads = test_overloads(n_verts, n_edges, seed);
std::cout << "overloads flow: " << flow_overloads << std::endl;
BOOST_CHECK(flow_bundles == flow_overloads);
/*
* excessive test version where kolmogorov's algorithm invariants are checked
*/
long flow_invariants = test_algorithms_invariant(n_verts, n_edges, seed);
std::cout << "invariants flow: " << flow_invariants << std::endl;
BOOST_CHECK(flow_overloads == flow_invariants);
return 0;
}

136
test/max_flow_test.cpp Normal file
View File

@ -0,0 +1,136 @@
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <vector>
#include <iterator>
#include <iostream>
#include <algorithm>
#include <fstream>
#include <boost/test/minimal.hpp>
//three max_flows we test here
#include <boost/graph/kolmogorov_max_flow.hpp>
#include <boost/graph/push_relabel_max_flow.hpp>
#include <boost/graph/edmunds_karp_max_flow.hpp>
//boost utilities we use
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/random.hpp>
#include <boost/property_map.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/lexical_cast.hpp>
/***************
* test which compares results of the three different max_flow implementations
* command line parameters:
* number_of_vertices: defaults to 100
* number_of_edges: defaults to 1000
* seeed: defaults to 1
***************/
using namespace boost;
int test_main(int argc, char* argv[])
{
typedef adjacency_list_traits < vecS, vecS, directedS > Traits;
typedef adjacency_list < vecS, vecS, directedS,
property < vertex_index_t, long,
property < vertex_color_t, boost::default_color_type,
property < vertex_distance_t, long,
property < vertex_predecessor_t, Traits::edge_descriptor > > > >,
property < edge_capacity_t, long,
property < edge_residual_capacity_t, long,
property < edge_reverse_t, Traits::edge_descriptor > > > > Graph;
typedef graph_traits<Graph>::edge_descriptor tEdge;
typedef graph_traits<Graph>::vertex_descriptor tVertex;
graph_traits<Graph>::vertices_size_type n_verts = 100;
graph_traits<Graph>::edges_size_type n_edges = 1000;
std::size_t seed = 1;
if (argc > 1) n_verts = lexical_cast<std::size_t>(argv[1]);
if (argc > 2) n_edges = lexical_cast<std::size_t>(argv[2]);
if (argc > 3) seed = lexical_cast<std::size_t>(argv[3]);
Graph g;
const int cap_low = 1;
const int cap_high = 1000;
//init random numer generator
minstd_rand gen(seed);
//generate graph
generate_random_graph(g, n_verts, n_edges, gen);
//init an uniform distribution int generator
typedef variate_generator<minstd_rand, uniform_int<int> > tIntGen;
tIntGen int_gen(gen, uniform_int<int>(cap_low, cap_high));
//init edge-capacities
randomize_property<edge_capacity_t, Graph, tIntGen> (g,int_gen);
//get source and sink node
tVertex source_vertex = random_vertex(g, gen);
tVertex sink_vertex = graph_traits<Graph>::null_vertex();
while(sink_vertex == graph_traits<Graph>::null_vertex() || sink_vertex == source_vertex)
sink_vertex = random_vertex(g, gen);
//add reverse edges (ugly... how to do better?!)
property_map < Graph, edge_reverse_t >::type rev = get(edge_reverse, g);
property_map < Graph, edge_capacity_t >::type cap = get(edge_capacity, g);
std::list<tEdge> edges_copy;
graph_traits<Graph>::edge_iterator ei, e_end;
tie(ei, e_end) = edges(g);
std::copy(ei, e_end, std::back_insert_iterator< std::list<tEdge> >(edges_copy));
while( ! edges_copy.empty()){
tEdge old_edge=edges_copy.front();
edges_copy.pop_front();
tVertex source_vertex = target(old_edge, g);
tVertex target_vertex = source(old_edge, g);
bool inserted;
tEdge new_edge;
tie(new_edge,inserted) = add_edge(source_vertex, target_vertex, g);
assert(inserted);
rev[old_edge] = new_edge;
rev[new_edge] = old_edge ;
cap[new_edge] = 0;
}
typedef property_traits< property_map<Graph, edge_capacity_t>::const_type>::value_type tEdgeVal;
tEdgeVal kolmo = kolmogorov_max_flow(g,source_vertex,sink_vertex);
tEdgeVal push_relabel = push_relabel_max_flow(g,source_vertex,sink_vertex);
tEdgeVal edmunds_karp = edmunds_karp_max_flow(g,source_vertex,sink_vertex);
BOOST_REQUIRE( kolmo == push_relabel );
BOOST_REQUIRE( push_relabel == edmunds_karp );
return 0;
}