[Lemon-commits] Peter Kovacs: Fully rework and extend the adapto...

Lemon HG hg at lemon.cs.elte.hu
Mon Feb 22 19:46:51 CET 2010


details:   http://lemon.cs.elte.hu/hg/lemon-tutorial/rev/31a1a79019bb
changeset: 39:31a1a79019bb
user:      Peter Kovacs <kpeter [at] inf.elte.hu>
date:      Sun Feb 21 15:07:59 2010 +0100
description:
	Fully rework and extend the adaptors section

diffstat:

 adaptors.dox |  429 ++++++++++++++++++++++++++++++++++++++++++++----------------
 toc.txt      |    3 +
 2 files changed, 314 insertions(+), 118 deletions(-)

diffs (truncated from 501 to 300 lines):

diff --git a/adaptors.dox b/adaptors.dox
--- a/adaptors.dox
+++ b/adaptors.dox
@@ -20,81 +20,106 @@
 /**
 [PAGE]sec_graph_adaptors[PAGE] Graph Adaptors
 
-\todo Clarify this section.
+In typical algorithms and applications related to graphs and networks,
+we usually encounter situations in which a specific alteration of a graph
+has to be considered.
+If some nodes or arcs have to be hidden (maybe temporarily) or the reverse
+oriented graph has to be used, then this is the case.
+However, actually modifing physical storage of the graph or
+making a copy of the graph structure along with the required maps
+could be rather expensive (in time or in memory usage) compared to the
+operations that should be performed on the altered graph.
+In such cases, the LEMON \e graph \e adaptor \e classes could be used.
 
-Alteration of standard containers need a very limited number of
-operations, these together satisfy the everyday requirements.
-In the case of graph structures, different operations are needed which do
-not alter the physical graph, but gives another view. If some nodes or
-arcs have to be hidden or the reverse oriented graph have to be used, then
-this is the case. It also may happen that in a flow implementation
-the residual graph can be accessed by another algorithm, or a node-set
-is to be shrunk for another algorithm.
-LEMON also provides a variety of graphs for these requirements called
-\ref graph_adaptors "graph adaptors". Adaptors cannot be used alone but only
-in conjunction with other graph representations.
 
-The main parts of LEMON are the different graph structures, generic
-graph algorithms, graph concepts, which couple them, and graph
-adaptors. While the previous notions are more or less clear, the
-latter one needs further explanation. Graph adaptors are graph classes
-which serve for considering graph structures in different ways.
+[SEC]sec_reverse_digraph[SEC] Reverse Oriented Digraph
 
-A short example makes this much clearer.  Suppose that we have an
-instance \c g of a directed graph type, say ListDigraph and an algorithm
+Let us suppose that we have an instance \c g of a directed graph type, say
+\ref ListDigraph and an algorithm
 \code
-template <typename Digraph>
-int algorithm(const Digraph&);
+  template <typename Digraph>
+  int algorithm(const Digraph&);
 \endcode
-is needed to run on the reverse oriented graph.  It may be expensive
-(in time or in memory usage) to copy \c g with the reversed
-arcs.  In this case, an adaptor class is used, which (according
-to LEMON \ref concepts::Digraph "digraph concepts") works as a digraph.
-The adaptor uses the original digraph structure and digraph operations when
-methods of the reversed oriented graph are called.  This means that the adaptor
-have minor memory usage, and do not perform sophisticated algorithmic
-actions.  The purpose of it is to give a tool for the cases when a
-graph have to be used in a specific alteration.  If this alteration is
-obtained by a usual construction like filtering the node or the arc set or
-considering a new orientation, then an adaptor is worthwhile to use.
-To come back to the reverse oriented graph, in this situation
+is needed to run on the reverse oriented digraph.
+In this situation, a certain adaptor class
 \code
-template<typename Digraph> class ReverseDigraph;
+  template <typename Digraph>
+  class ReverseDigraph;
 \endcode
-template class can be used. The code looks as follows
+can be used.
+
+The graph adaptors are special classes that serve for considering other graph
+structures in different ways. They can be used exactly the same as "real"
+graphs, i.e. they conform to the \ref graph_concepts "graph concepts", thus all
+generic algorithms can be performed on them. However, the adaptor classes
+cannot be used alone but only in conjunction with actual graph representations.
+They do not alter the physical graph storage, they just give another view of it.
+When the methods of the adaptors are called, they use the underlying
+graph structures and their operations, thus these classes have only negligible
+memory usage and do not perform sophisticated algorithmic actions.
+
+This technique yields convenient tools that help writing compact and elegant
+code, and makes it possible to easily implement complex algorithms based on
+well tested standard components.
+
+For solving the problem introduced above, we could use the follwing code.
+
 \code
-ListDigraph g;
-ReverseDigraph<ListDigraph> rg(g);
-int result = algorithm(rg);
+  ListDigraph g;
+  ReverseDigraph<ListDigraph> rg(g);
+  int result = algorithm(rg);
 \endcode
-During running the algorithm, the original digraph \c g is untouched.
-This techniques give rise to an elegant code, and based on stable
-graph adaptors, complex algorithms can be implemented easily.
 
-In flow, circulation and matching problems, the residual
-graph is of particular importance. Combining an adaptor implementing
-this with shortest path algorithms or minimum mean cycle algorithms,
-a range of weighted and cardinality optimization algorithms can be
-obtained. For other examples, the interested user is referred to the
-detailed documentation of particular adaptors.
+Note that the original digraph \c g remains untouched during the whole
+procedure.
 
-The behavior of graph adaptors can be very different. Some of them keep
-capabilities of the original graph while in other cases this would be
-meaningless. This means that the concepts that they meet depend
-on the graph adaptor, and the wrapped graph.
-For example, if an arc of a reversed digraph is deleted, this is carried
-out by deleting the corresponding arc of the original digraph, thus the
-adaptor modifies the original digraph.
-However in case of a residual digraph, this operation has no sense.
+LEMON also provides simple "creator functions" for the adaptor
+classes to make their usage even simpler.
+For example, \ref reverseDigraph() returns an instance of \ref ReverseDigraph,
+thus the above code can be written like this.
 
-Let us stand one more example here to simplify your work.
-ReverseDigraph has constructor
 \code
-ReverseDigraph(Digraph& digraph);
+  ListDigraph g;
+  int result = algorithm(reverseDigraph(g));
 \endcode
-This means that in a situation, when a <tt>const %ListDigraph&</tt>
-reference to a graph is given, then it have to be instantiated with
-<tt>Digraph=const %ListDigraph</tt>.
+
+Another essential feature of the adaptors is that their \c Node and \c Arc
+types convert to the original item types.
+Therefore, the maps of the original graph can be used in connection with
+the adaptor.
+
+In the following code, Dijksta's algorithm is run on the reverse oriented
+graph but using the original node and arc maps.
+
+\code
+  ListDigraph g;
+  ListDigraph::ArcMap length(g);
+  ListDigraph::NodeMap dist(g);
+
+  ListDigraph::Node s = g.addNode();
+  // add more nodes and arcs
+
+  dijkstra(reverseDigraph(g), length).distMap(dist).run(s);
+\endcode
+
+In the above examples, we used \ref ReverseDigraph in such a way that the
+underlying digraph was not changed. However, the adaptor class can even be
+used for modifying the original graph structure.
+It allows adding and deleting arcs or nodes, and these operations are carried
+out by calling suitable functions of the underlying digraph (if it supports
+them).
+
+For this, \ref ReverseDigraph "ReverseDigraph<GR>" has a constructor of the
+following form.
+\code
+  ReverseDigraph(GR& gr);
+\endcode
+
+This means that in a situation, when the modification of the original graph
+has to be avoided (e.g. it is given as a const reference), then the adaptor
+class has to be instantiated with \c GR set to be \c const type
+(e.g. <tt>GR = const %ListDigraph</tt>), as in the following example.
+
 \code
 int algorithm1(const ListDigraph& g) {
   ReverseDigraph<const ListDigraph> rg(g);
@@ -102,41 +127,163 @@
 }
 \endcode
 
-<hr>
+\note Modification capabilities are not supported for all adaptors.
+E.g. for \ref ResidualDigraph (see \ref sec_other_adaptors "later"),
+this makes no sense.
 
-The LEMON graph adaptor classes serve for considering graphs in
-different ways. The adaptors can be used exactly the same as "real"
-graphs (i.e., they conform to the graph concepts), thus all generic
-algorithms can be performed on them. However, the adaptor classes use
-the underlying graph structures and operations when their methods are
-called, thus they have only negligible memory usage and do not perform
-sophisticated algorithmic actions. This technique yields convenient and
-elegant tools for the cases when a graph has to be used in a specific
-alteration, but copying it would be too expensive (in time or in memory
-usage) compared to the algorithm that should be executed on it. The
-following example shows how the \ref ReverseDigraph adaptor can be used
-to run Dijksta's algorithm on the reverse oriented graph. Note that the
-maps of the original graph can be used in connection with the adaptor,
-since the node and arc types of the adaptors convert to the original
-item types.
+As a more complex example, let us see how \ref ReverseDigraph can be used
+together with a graph search algorithm to decide whether a directed graph is
+strongly connected or not.
+We exploit the fact the a digraph is strongly connected if and only if
+for an arbitrarily selected node \c u, each other node is reachable from
+\c u (along a directed path) and \c u is reachable from each node.
+The latter condition is the same that each node is reachable from \c u
+in the reversed digraph.
 
 \code
-dijkstra(reverseDigraph(g), length).distMap(dist).run(s);
+  template <typename Digraph>
+  bool stronglyConnected(const Digraph& g) {
+    typedef typename Digraph::NodeIt NodeIt;
+    NodeIt u(g);
+    if (u == INVALID) return true;
+
+    // Run BFS on the original digraph
+    Bfs<Digraph> bfs(g);
+    bfs.run(u);
+    for (NodeIt n(g); n != INVALID; ++n) {
+      if (!bfs.reached(n)) return false;
+    }
+
+    // Run BFS on the reverse oriented digraph
+    typedef ReverseDigraph<const Digraph> RDigraph;
+    RDigraph rg(g);
+    Bfs<RDigraph> rbfs(rg);
+    rbfs.run(u);
+    for (NodeIt n(g); n != INVALID; ++n) {
+      if (!rbfs.reached(n)) return false;
+    }
+
+    return true;
+  }
 \endcode
 
-Using \ref ReverseDigraph could be as efficient as working with the
-original graph, but not all adaptors can be so fast, of course. For
-example, the subgraph adaptors have to access filter maps for the nodes
-and/or the arcs, thus their iterators are significantly slower than the
-original iterators. LEMON also provides some more complex adaptors, for
-instance, \ref SplitNodes, which can be used for splitting each node in
-a directed graph and \ref ResidualDigraph for modeling the residual
-network for flow and matching problems.
+Note that we have to use the adaptor with '<tt>const Digraph</tt>' type, since
+\c g is a \c const reference to the original graph structure.
+The \ref stronglyConnected() function provided in LEMON has a quite
+similar implementation.
 
-Therefore, in cases when rather complex algorithms have to be used
-on a subgraph (e.g. when the nodes and arcs have to be traversed several
-times), it could worth copying the altered graph into an efficient structure
-and run the algorithm on it.
+
+[SEC]sec_subgraphs[SEC] Subgraph Adaptorts
+
+Another typical requirement is the use of certain subgraphs of a graph,
+or in other words, hiding nodes and/or arcs from a graph.
+LEMON provides several convenient adaptors for these purposes.
+
+\ref FilterArcs can be used when some arcs have to be hidden from a digraph.
+A \e filter \e map has to be given to the constructor, which assign \c bool
+values to the arcs specifying whether they have to be shown or not in the
+subgraph structure.
+Suppose we have a \ref ListDigraph structure \c g.
+Then we can construct a subgraph in which some arcs (\c a1, \c a2 etc.)
+are hidden as follows.
+
+\code
+  ListDigraph::ArcMap filter(g, true);
+  filter[a1] = false;
+  filter[a2] = false;
+  // ...
+  FilterArcs<ListDigraph> subgraph(g, filter);
+\endcode
+
+The following more complex code runs Dijkstra's algorithm on a digraph
+that is obtained from another digraph by hiding all arcs having negative
+lengths.
+
+\code
+  ListDigraph::ArcMap<int> length(g);
+  ListDigraph::NodeMap<int> dist(g);
+
+  dijkstra(filterArcs( g, lessMap(length, constMap<ListDigraph::Arc>(0)) ),
+           length).distMap(dist).run(s);
+\endcode
+
+Note the extensive use of map adaptors and creator functions, which makes
+the code really compact and elegant.
+
+\note Implicit maps and graphs (e.g. created using functions) can only be
+used with the function-type interfaces of the algorithms, since they store
+only references for the used structures.
+
+\ref FilterEdges can be used for hiding edges from an undirected graph (like
+\ref FilterArcs is used for digraphs). \ref FilterNodes serves for filtering
+nodes along with the incident arcs or edges in a directed or undirected graph.
+If both arcs/edges and nodes have to be hidden, then you could use
+\ref SubDigraph or \ref SubGraph adaptors.
+
+\code
+  ListGraph ug;
+  ListGraph::NodeMap<bool> node_filter(ug);



More information about the Lemon-commits mailing list