From 0c3f9bb47931bae82e769667aedc2157ce02c0a8 Mon Sep 17 00:00:00 2001 From: nobody Date: Mon, 16 Aug 2004 14:12:16 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'merged_to_graph_devel'. [SVN r24507] --- doc/betweenness_centrality_clustering.html | 22 + doc/brandes_betweenness_centrality.html | 278 ++++++++ doc/circle_layout.html | 14 + doc/figs/betweenness_centrality.gif | Bin 0 -> 492 bytes doc/figs/central_point_dominance.gif | Bin 0 -> 786 bytes doc/figs/relative_betweenness_centrality.gif | Bin 0 -> 224 bytes doc/figs/sigma_st.gif | Bin 0 -> 73 bytes doc/figs/sigma_stv.gif | Bin 0 -> 129 bytes doc/figs/v_star.gif | Bin 0 -> 71 bytes doc/figs/wheel_graph.gif | Bin 0 -> 7576 bytes doc/kamada_kawai_spring_layout.html | 44 ++ doc/layout_tolerance.html | 14 + example/actor_clustering.cpp | 187 ++++++ .../betweenness_centrality_clustering.hpp | 165 +++++ .../graph/brandes_betweenness_centrality.hpp | 599 ++++++++++++++++++ include/boost/graph/circle_layout.hpp | 53 ++ .../graph/kamada_kawai_spring_layout.hpp | 506 +++++++++++++++ test/brandes_betweenness_centrality_test.cpp | 479 ++++++++++++++ test/layout_test.cpp | 258 ++++++++ 19 files changed, 2619 insertions(+) create mode 100644 doc/betweenness_centrality_clustering.html create mode 100644 doc/brandes_betweenness_centrality.html create mode 100644 doc/circle_layout.html create mode 100644 doc/figs/betweenness_centrality.gif create mode 100644 doc/figs/central_point_dominance.gif create mode 100644 doc/figs/relative_betweenness_centrality.gif create mode 100644 doc/figs/sigma_st.gif create mode 100644 doc/figs/sigma_stv.gif create mode 100644 doc/figs/v_star.gif create mode 100644 doc/figs/wheel_graph.gif create mode 100644 doc/kamada_kawai_spring_layout.html create mode 100644 doc/layout_tolerance.html create mode 100644 example/actor_clustering.cpp create mode 100644 include/boost/graph/betweenness_centrality_clustering.hpp create mode 100644 include/boost/graph/brandes_betweenness_centrality.hpp create mode 100644 include/boost/graph/circle_layout.hpp create mode 100644 include/boost/graph/kamada_kawai_spring_layout.hpp create mode 100644 test/brandes_betweenness_centrality_test.cpp create mode 100644 test/layout_test.cpp diff --git a/doc/betweenness_centrality_clustering.html b/doc/betweenness_centrality_clustering.html new file mode 100644 index 00000000..d166d487 --- /dev/null +++ b/doc/betweenness_centrality_clustering.html @@ -0,0 +1,22 @@ +Function betweenness_centrality_clustering

Function betweenness_centrality_clustering

boost::betweenness_centrality_clustering — Graph clustering based on edge betweenness centrality.

Synopsis

+template<typename MutableGraph, typename Done, typename EdgeCentralityMap, 
+         typename VertexIndexMap> 
+  void betweenness_centrality_clustering(MutableGraph & g, Done done, 
+                                         EdgeCentralityMap edge_centrality, 
+                                         VertexIndexMap vertex_index);
+template<typename MutableGraph, typename Done, typename EdgeCentralityMap> 
+  void betweenness_centrality_clustering(MutableGraph & g, Done done, 
+                                         EdgeCentralityMap edge_centrality);
+template<typename MutableGraph, typename Done> 
+  void betweenness_centrality_clustering(MutableGraph & g, Done done);

Description

Parameters

done

The function object that indicates termination of the algorithm. It must be a ternary function object thats accepts the maximum centrality, the descriptor of the edge that will be removed, and the graph g .

edge_centrality

(UTIL/OUT) The property map that will store the betweenness centrality for each edge. When the algorithm terminates, it will contain the edge centralities for the graph. The type of this property map must model the ReadWritePropertyMap concept. Defaults to an iterator_property_map whose value type is Done::centrality_type and using get(edge_index, g) for the index map.

g

The graph on which clustering will be performed. The type of this parameter (MutableGraph ) must be a model of the VertexListGraph, IncidenceGraph, EdgeListGraph, and Mutable Graph concepts.

vertex_index

(IN) The property map that maps vertices to indices in the range [0, num_vertices(g)). This type of this property map must model the ReadablePropertyMap concept and its value type must be an integral type. Defaults to get(vertex_index, g) .

This algorithm implements graph clustering based on edge betweenness centrality. It is an iterative algorithm, where in each step it compute the edge betweenness centrality (via brandes_betweenness_centrality) and removes the edge with the maximum betweenness centrality. The done function object determines when the algorithm terminates (the edge found when the algorithm terminates will not be removed).


+ + + +
Copyright © 2004 +Douglas Gregor, Indiana University (dgregor@cs.indiana.edu)
+Andrew Lumsdaine, +Indiana University (lums@osl.iu.edu) +
+ + diff --git a/doc/brandes_betweenness_centrality.html b/doc/brandes_betweenness_centrality.html new file mode 100644 index 00000000..ff0c549d --- /dev/null +++ b/doc/brandes_betweenness_centrality.html @@ -0,0 +1,278 @@ + + + + Boost Graph Library: Brandes' Betweenness Centrality + + + + C++ Boost +

brandes_betweenness_centrality

+ +

+

+// named parameter versions
+template<typename Graph, typename Param, typename Tag, typename Rest>
+void 
+brandes_betweenness_centrality(const Graph& g,
+                               const bgl_named_params<Param,Tag,Rest>& params);
+
+template<typename Graph, typename CentralityMap>
+void 
+brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map);
+
+template<typename Graph, typename CentralityMap, typename EdgeCentralityMap>
+void 
+brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map,
+                               EdgeCentralityMap edge_centrality);
+
+// non-named parameter versions
+template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+         typename IncomingMap, typename DistanceMap, typename DependencyMap, 
+         typename PathCountMap, typename VertexIndexMap>
+void 
+brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map,
+                               EdgeCentralityMap edge_centrality,
+                               IncomingMap incoming,
+                               DistanceMap distance, DependencyMap dependency,
+                               PathCountMap path_count, 
+                               VertexIndexMap vertex_index);
+
+template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+         typename IncomingMap, typename DistanceMap, typename DependencyMap, 
+         typename PathCountMap, typename VertexIndexMap, typename WeightMap>    
+void 
+brandes_betweenness_centrality(const Graph& g, CentralityMap centrality_map,
+                               EdgeCentralityMap edge_centrality,
+                               IncomingMap incoming, 
+                               DistanceMap distance,  DependencyMap dependency,
+                               PathCountMap path_count,      
+                               VertexIndexMap vertex_index,
+                               WeightMap weight_map);
+
+// helper functions
+template<typename Graph, typename CentralityMap>
+void 
+relative_betweenness_centrality(const Graph& g, CentralityMap centrality_map);
+
+template<typename Graph, typename CentralityMap>
+typename property_traits<CentralityMap>::value_type
+central_point_dominance(const Graph& g, CentralityMap centrality_map);
+    
+ +

This algorithm [54] +computes the betweenness centrality [55,56] of each vertex or each +edge in the graph. The betweenness centrality of a vertex v +is defined by + +

, + +

where is the number of shortest paths from +vertex s to vertex t and +is the number of shortest paths from vertex s to vertex +t that pass through vertex v. + + + +

The edge betweenness centrality indicates for each edge the +betweenness centrality that was contributed to the target(s) of the +edge (plural for undirected graphs). Similar to (vertex) betweenness +centrality, edge betweenness centrality can be used to determine the +edges through which most shortest paths must pass. A single invocation +of this algorithm can compute either the vertex or edge centrality (or +both).

+ +

This algorithm can operate either on weighted graphs (if a suitable +edge weight map is supplied) or unweighted graphs (if no edge weight +map is supplied). The result is the absolute betweenness centrality; +to convert to the relative betweenness centrality, which scales each +absolute centrality by +(where n is the number of vertices in the graph), use +relative_betweenness_centrality. Given the relative +betweenness centrality, one can compute the central point +dominance [55], which is a measure of the maximum "betweenness" of any +point in the graph: it will be 0 for complete graphs and +1 for "wheel" graphs (in which there is a central vertex that all +paths include; see Fig. 1). Let be the vertex with the largest relative betweenness centrality; then, the central point dominance is defined as: + +

+ + + +

+ + + + + + + + + + +
Fig. 1: A wheel graph, where every path travels through the central node.
The central point dominance of this graph is 1. +
+ +

Where Defined

+boost/graph/brandes_betweenness_centrality.hpp + +

Parameters

+IN: const Graph& g +
+ The graph object on which the algorithm will be applied. The type + Graph must be a model of Vertex List Graph and Incidence Graph. When an edge + centrality map is supplied, it must also model Edge List Graph. +
+ +UTIL: IncomingMap incoming +
+ This property map records the set of edges incoming to each vertex that comprise a shortest path from a particular source vertex through this vertex, and is used internally by the algorithm.The IncomingMap type must be a Lvalue Property + Map whose key type is the same as the vertex descriptor type of + the graph and whose value type is a Sequence (e.g., an + std::vector) containing edge descriptors.
+ + Default: + iterator_property_map created from a + std::vector of std::vector<Edge>, where + Edge is the edge descriptor type of the graph. +
+ +UTIL: DistanceMap distance_map +
+ The shortest path weight from each source vertex s to each + vertex in the graph g is recorded in this property map, but + the result is only used internally. The type DistanceMap + must be a model of Read/Write + Property Map. The vertex descriptor type of the graph needs to + be usable as the key type of the distance map. The value type of the + distance map is the element type of a Monoid.
+ + Default: + iterator_property_map created from a + std::vector of the WeightMap's value type (or the + vertices_size_type of the graph when no weight map exists) + of size num_vertices(g) and using the vertex_index for + the index map. +
+ +UTIL: DependencyMap dependency +
+ Property map used internally to accumulate partial betweenness + centrality results. The type DependencyMap must be a model + of Read/Write + Property Map. The vertex descriptor type of the graph needs to + be usable as the key type of the dependency map. The value type of + the dependency map must be compatible with the value type of the + centrality map.
+ + Default: + iterator_property_map created from a + std::vector of the CentralityMap's value type of + size num_vertices(g) and using the vertex_index + for the index map. +
+ +UTIL: PathCountMap path_count +
+ Property map used internally to accumulate the number of paths that + pass through each particular vertex. The type PathCountMap + must be a model of Read/Write + Property Map. The vertex descriptor type of the graph needs to + be usable as the key type of the dependency map. The value type of + the dependency map must be an integral type large enough to store + the number of paths in the graph.
+ + Default: + iterator_property_map created from a + std::vector of the degree_size_type of the graph of + size num_vertices(g) and using the vertex_index + for the index map. +
+ +

Named parameters

+OUT/UTIL: CentralityMap centrality_map +
+ This property map is used to accumulate the betweenness centrality + of each vertex, and is the primary output of the algorithm. The type + CentralityMap must be a model of Read/Write + Property Map, with the graph's vertex descriptor type as its key + type. The value type of this property map should be a floating-point + or rational type.
+ + Default: a dummy_property_map, which requires no + work to compute and returns no answer. +
+ +OUT/UTIL: EdgeCentralityMap edge_centrality +
+ This property map is used to accumulate the betweenness centrality + of each edge, and is a secondary form of output for the + algorithm. The type EdgeCentralityMap must be a model of Read/Write + Property Map, with the graph's edge descriptor type as its key + type. The value type of this property map should be the same as the + value type of the CentralityMap property map.
+ + Default: a dummy_property_map, which requires no + work to compute and returns no answer. +
+ +IN: vertex_index_map(VertexIndexMap vertex_index) +
+ This maps each vertex to an integer in the range [0, + num_vertices(g)). This is necessary for efficient updates of the + heap data structure when an edge is relaxed. The type + VertexIndexMap must be a model of + Readable Property Map. The value type of the map must be an + integer type. The vertex descriptor type of the graph needs to be + usable as the key type of the map.
+ Default: get(vertex_index, g) +
+ +IN: weight_map(WeightMap w_map) +
+ The weight or ``length'' of each edge in the graph. The weights + must all be non-negative, and the algorithm will throw a + negative_edge + exception is one of the edges is negative. + The type WeightMap must be a model of + Readable Property Map. The edge descriptor type of + the graph needs to be usable as the key type for the weight + map. The value type for this map must be + the same as the value type of the distance map.
+ Default: All edge weights are assumed to be equivalent. +
+ +

Complexity

+The time complexity is O(VE) for unweighted graphs and +O(VE + V(V+E) log V) for weighted graphs. The space complexity +is O(VE). + +
+ + + +
Copyright © 2004 +Douglas Gregor, Indiana University (dgregor@cs.indiana.edu)
+Andrew Lumsdaine, +Indiana University (lums@osl.iu.edu) +
+ +Last modified: Wed Aug 4 17:41:44 EST 2004 + + diff --git a/doc/circle_layout.html b/doc/circle_layout.html new file mode 100644 index 00000000..a70211c3 --- /dev/null +++ b/doc/circle_layout.html @@ -0,0 +1,14 @@ +Function template circle_graph_layout
c++boost.gif (8819 bytes)HomeLibrariesPeopleFAQMore

Function template circle_graph_layout

boost::circle_graph_layout — Layout the graph with the vertices at the points of a regular n-polygon.

Synopsis

+template<typename VertexListGraph, typename PositionMap, typename Radius> 
+  void circle_graph_layout(const VertexListGraph & g, PositionMap position, 
+                           Radius radius);

Where Defined

boost/graph/circle_layout.hpp

Description

The distance from the center of the polygon to each point is determined by the radius parameter. The position parameter must be an Lvalue Property Map whose value type is a class type containing x and y members that will be set to the x and y coordinates.


+ + + +
Copyright © 2004 +Douglas Gregor, Indiana University (dgregor@cs.indiana.edu)
+Andrew Lumsdaine, +Indiana University (lums@osl.iu.edu) +
+ diff --git a/doc/figs/betweenness_centrality.gif b/doc/figs/betweenness_centrality.gif new file mode 100644 index 0000000000000000000000000000000000000000..f91fced2ddc202fd4c11f4989545212b57dfff96 GIT binary patch literal 492 zcmZ?wbhEHbEN5_JXkY+=|Ns9h{$ycfU|?j>0r5dH4ov@g`d6NQ%fFb>=t9?Kw*}Sf zOHY?~baK|dIyLEb$y7s|LgTdyuJyLRr0i_!`|)9V)~YP?72YrLZjs;!nVKo!J&Cs zbmn%^u#Soy!X23w7EdzGZ!Y)DteSPHv)wB)YRRcvmw%Ki?G~TSWu>)qm-emOCel37 zntznb)RO)@YQHtZ$L5$$PqgimtlF-*cX#og@x7{aZ<7U|-0PONdqqpSzsa7+&SXs9 z_p{P}-=|}qoBIx*oVTLk5yQM}@wc}RwqD(%nsVYC`w_GMOMI7n4!GxXq;aML=OxyU zU0Sw@$95#o$a~Ub5$?bz&AxfjG2h90Z#J*^7__YPtigmmZYNadnebJga`d>8)>C0z zYZJzAZeW^1F0q*qKVeezZJ*v6vCJ9LA} zPu{hwJTJ6+<%$Q3x5{Yh_1-SezQED(dD)VqD`XcOHT`^%*X(`fZAZ`gebuZ*XCGVs yW%FB|Sb0C>is5XpqviaWp8Hm0XTFWzRr>buw=iqBY1u5#j#MZM)dw&zSOWmGCGCd* literal 0 HcmV?d00001 diff --git a/doc/figs/central_point_dominance.gif b/doc/figs/central_point_dominance.gif new file mode 100644 index 0000000000000000000000000000000000000000..29aa67e99d1bc7aeb51d789484e6d263b781f246 GIT binary patch literal 786 zcmZ?wbhEHb)MhkgXkY+=|Ns9h{$ycfU|?j>0r5dH4ov@g`d6NQ%fEQemY@Xjb2iQP zxf5?pRr@mWdDW7Btwv9+Qyevq*F3bE{rAwtCn`eFa@w@TWJw8Iuiw_y zslkb+RgKA0Lk~YzyB?lXdd#j;_w$dXR(IMKmC8ouifdSSL}X-qXRp|C;)X=Pa|umL z&z;kcI&(zsJo&P1(agwY9j?>a%PXdRTg-9F;8rH*>|ZyhJ@X1{;gOwz}PqCKZ{b0RbT+%Q{r$F)*u(%f!y z{q4J}esg`{zMBhb3GnJ<& zIj4Pm7QguQk>Ao&?W&Vfg{R)J2>8~TAQE?cU5%DelHIwaxx$iPr)@dVGy5(3zv|l` zKfiWAzW>HO@1;|x%IiLQ*>qn%P;^JM!Jpd4w*;<v`j5a4{Z*wtivZG}v(jMS$`NsDEtEB2MKx^UK-err)SKAUTrd0dH|CRJToD{3}cWv$+{&n>I4hb@eO!5RR?mv_nl literal 0 HcmV?d00001 diff --git a/doc/figs/relative_betweenness_centrality.gif b/doc/figs/relative_betweenness_centrality.gif new file mode 100644 index 0000000000000000000000000000000000000000..f296730f8eed54a92357b50241ab3ad81c49a4e2 GIT binary patch literal 224 zcmZ?wbhEHb3}#SfXkY+=|Ns9h{$ycfU|?j>0r5dH4ous7`d6Oj;GZ>TOVm%FJ$ruh zx1AA7Yy3HpJ9E)Qt*kfC<_XWaV%YKSA3qOQ`xJ3+wwsO_%O9V;r2H(yV&zuBy;-uS zUwz}|>$aZx#=>nbTWMNrINNoZTL-TFvDJUHw&cZ4`S0piH>v-)oW3GtM#Q5dKR6^c zHLrzcR(8%@x$|VFh2@SPk%{foj-*>jbxl3id~ny5lJv)qCq0pj5?ft8`DK>#j!V)y b-CM$TcUv9OZhzyMcI4KU_HfAn1_o;YQ21uT literal 0 HcmV?d00001 diff --git a/doc/figs/sigma_st.gif b/doc/figs/sigma_st.gif new file mode 100644 index 0000000000000000000000000000000000000000..f3bfe121c414f14c90a70b62a159a83aca6fa37b GIT binary patch literal 73 zcmZ?wbhEHb6lUOLXkY+=|Ns9h{$ycfU|?j>0r5dH4onI@4V=f`_VERuJ|a1F&ic?- Xs$$1V7pC`J58QbBR_gsg1_o;Y!Oj=7 literal 0 HcmV?d00001 diff --git a/doc/figs/sigma_stv.gif b/doc/figs/sigma_stv.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d01cc0545b9b49e162680575534ef63eb24d4f9 GIT binary patch literal 129 zcmZ?wbhEHb)L;-~XkY+=|Ns9h{$ycfU|?j>0r5dH4ond}{VO@D|IRQrXt<^4TUGTk z^#1kU3#~;fGghXWOxoCSZF9*b&7;?T-@auKvFd@Jhte^TQxA=<=-7$dN~{r?y3I;| e_S9c1lqw_cPJ70)Om@~bDT|YO4}F{&8LR=mUo-#! literal 0 HcmV?d00001 diff --git a/doc/figs/v_star.gif b/doc/figs/v_star.gif new file mode 100644 index 0000000000000000000000000000000000000000..880d456cbceabf779e8b3e7853fb2074bdeb7bb7 GIT binary patch literal 71 zcmZ?wbhEHb0r5dH4oq@A{fs-Vy*w8z(B!>w)~1iM VVw861s%?$bjIxbi?#Rku4FHo26#W1I literal 0 HcmV?d00001 diff --git a/doc/figs/wheel_graph.gif b/doc/figs/wheel_graph.gif new file mode 100644 index 0000000000000000000000000000000000000000..20c622ecb4aa68298d26f2a96f4c817adc34da7f GIT binary patch literal 7576 zcmZ?wbhEHbv}F`zoXEiN9|Zm}fPvyq7DfgJMg|=a4|Nli|1^))xG)N zp5Od!k36P5>soUAK`~BA&~BNYyeG0#_PHq4_{7FdLe}it3FdhqtLp=+%Tww6U|72IZ{n26 zVbz7#`Z_?onJ&mKbOl>khGJlEhs%GEnmmXh^%75)T#cKvRZ-|+@~a|iArvzkz-Gom zoB;KWFW822E?@`wfH~j*Sqd>P9qcdj$;e?93sLL`R_wls(@t$tP7^E&q8z}&{~&gG zs@3>}3WD7N4$`S$7yM-ue3<-d(vj4PlUzimV~Ff-IFovNYQX>^^gFfPwv!1u}8^?f(KGA98@r zl!bcH;FPDf0$4r7KME|M1O^QqPzr#CVmm0jQp>MLW$Qw$0;OK484aLd(sKG>_`h`L zT&NhxTu|5+{bmNKpL#bod+k;phzKZ2L%hlla#(0t`P!}5oS-6$=WG!IIdi);Nb#w6 z>$YBt>Ht{|Nstgv8BgSp4-Rbs1<`M2h!7~=yubksPD0wdcVCOj)&$uN$!Xx2F4_$? zo8urT|3P91oL!k9W+;P<6^9rrkI>2s5}tm$7v#-saE$)t2RROsIomH7f9&CL-$j+zAAcZ^sGJ=J`B}cD|T8+}duHKRd9syM;k2A*oFK|HS@IQ2Ln- zc2^!G%R=Nd1s^sG)cC~ase{!UBh37!1rF&lNGc2mb0B47DmduCA*;6?95>LSZ@M7Z z5N3#qi$(KN+(EG zKxE<7dm$pR;Do*h6zxze)~o&pMF-T|C?zn&sJSOv`d7|&0^0LG(ud!3E~|>v67O!FefPW*+W7wAE5`inorGV$U?$b-pz6) z#F5J2^q~#W14%6QJw=z?z&Qe(-KO834ABY=XE)1Ch*mYQR!DGILoz_(l*vYr0%9^a zbn?L7xC=H66nLjPpu&*&HwOj9(>QoEr-B`v=MFJ~8SE&1B$Gka1=REGV69VJa+6;#|!frbburNq7id-yvfE6KZA zYC?jc9#Zc6g6)I^r+rUR5F`Y@v4gz?(P|EFlW6{j=!MiIcOZJft^=j^TyUsCYPadT z!5Pg5Y&s~hKpGj)VioL{NU#c!bH6~G3oZS?<%c#r_^cqI`XAEF0GC*naGfHMECJOC z30$~eE_r}s4O$$_g3|#ao2!DO30kN^$|f_oSEeF+<#sP9A%RQi|KPI56Ou3>VU!0> z4XN-9sR=O`nkhlybypPLWPvyroP%TY+(BspT4jK8GNkZ_W?2P(PRch-Wk%XLUK=j`w#SqW5}LsKB6{4G-hnJ)k~|9%IeHEJyY4tUv4 zP=o0XI6FfsT3r`VwecTPZ)}HU6DA3e$=|?^y?J-*wWw@HC1JZ|d0gNaItwW~C%fEG zf>f%YCg7g+U@a??MU_KSXoC5#$_JuoIvKravUQp;cnz3AG9k6TDl{vA|IXf0ahrO67l3>4dW>7WGn^c%EU80){TE1NK^n4WKw$)TJOAQ2Um)gy zs{kW#`3G(OLW~AyeQ-nZv^UuE&@v0umi}|&*1+l zOHkIDep?=FyE-^Rp@kZ#2!z&f;G%U7x{D`6gBEO<=ciuO=HNL`u#>^1RsWM@p&Cf$ ztOw;8aH|MfLCPyNJW1v{2uicy#wH@XAobl}eo&urdOyT8NZ$z5?t>IPkdo359N>_K zJE-w|4qBo?`W_HjNC1EX6YQNkAea3&ER95O`UN4R4NEIQ>GSkkMDLCl9GZ|~HWgeG zZ3jmgxM^*RTz1U_hZnS+1`14Da2b(T?*Nui1BW#<^MGPe7-BM{pf!V}UWnU3^$ny% zfOP+gAWdXQDg|kk25W`-a1uDAJRm`G7}orNHuB6NDGM5Ni~r9tfrKNZc?;>EdBe5N zh4=^3OQ{1pA5z4Do8_utbwHi#x(d>h1I6AoXj=f{ zO?jkNI;edTB@gxxv@a)RB_r^-=)TFRu$KPQeb{DxTg8O6%etphB1hQVN5r#l05U;1m$67{MYR z4CzFHI+))c^?+OGJRsK~YRX&P#o&Pc1S)PqA$4R=(WNZ?bCbZa2ri`{c?;51HU`&T z;3nBDa8Q8*CvJ{3$bjk7L8(I>=F0z>Tg9Kdf`smXY;{F;r82n81~-<>kzC0HDqT|{ z!4J-qS}?;hxAvVl?$Xr1G8tj&YT@zFcfQOEt)ujL^jTph(a+$sG^y#Q#Y`kf(y!d49uBH+OnNL%7Gq_hQv@K8)O2aMgQ3ku zXaNKcFmQZ<%h#8nwpuK-l7ut}-yzzPkaqEZq8sCoq5%|)Az*tT-8N`rd^Ofp&7BUg-H>&5yN^xCK?1u;#G~#C?z&FB+1Jd|ZBTs6ZMu+wFV64e^~&cSG#n+sQE- z(i($iE9fv9G|V7jX)UM#N}-`G;GFx<06Lfk=`rsGyQECr=wb4!WfPz(ppJ%gpdf~T z+TQ*-LmF^%!78BD zJ4EO&R0TA-LX#m_6D$uYLh=PT_5I%tsXDyqh7L5apfwI8l)wXOPs`LH^%V!WsIP%EBq5bAB;7)5_}_;?&Dkry z;70ULlvn`|K~BFdf6N)wgMoHnAYCwMp8_#<=7N-zAcLvjAz2l|+5X?Yrx#LYpMHBD z>?vr}LlW(6dAClm=xup5aAJpMAEc4Hw~#guB+)_h0;rVOa;qAwGaAx_`47$dppyJ8 zD>z6Y0fM3>8>|JIcHmC1%-nh%xp0NL9W|OT#tXD+fQ;sY+7;T+ni`ZYbD(hxu604> zQ07!{Qik->8bM0#K-%-*;PL{8FSy4Jt<%7@54iPl4JjLyw}N#-Cq6(Gk0>~AgUeB< zB(%*1lKcwE7T}sE6g)->?dyV^`&#)wIA=hzQ#hz?0X7C)ePp(RoeLQSdI#?O?S%9* z!GQ!R86l(7%;2Uhq_+w(cQv>L0;zdY6+n`Zao;?Ui$Wo}8#3$;2_&SE#~^SpKyr!O zB(%vQXgvpxs#MjP9}GiXAz2Vq#YRC!>>wT8nV=D0mr1^$$qZ0)GpriY8in@8YGFn2 ze{dp#3_U?h`7%iH0CMWpFtBS-l4$p5i07cy6xh3aE&Q=4+zhF@f6IeQ6Hp}vZnS}m zS!j{}8`AEx0T-D_-93;(Snmha$^myS)_!}0T_H3Kpgx2&F@qrvg477$1O{qHLdqUU zp$K;Q|GgH`V3m*%2S?D=xHH#!mO zEXdrV86+`58#&-m*FPtPlsMr=>Vw?_&6Q9iWx?)|h7={>n2DQH3W*0qizZ9|+$?a~ zLP~5`u)`DnB3N$C$2XD1P2hH`*RfWSxm>~`E ns?VFunction kamada_kawai_spring_layout
c++boost.gif (8819 bytes)HomeLibrariesPeopleFAQMore

Function kamada_kawai_spring_layout

boost::kamada_kawai_spring_layout — Kamada-Kawai spring layout for undirected graphs.

Synopsis

+template<typename Graph, typename PositionMap, typename WeightMap, typename T, 
+         bool EdgeOrSideLength, typename Done, typename VertexIndexMap, 
+         typename DistanceMatrix, typename SpringStrengthMatrix, 
+         typename PartialDerivativeMap> 
+  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position, 
+                                  WeightMap weight, 
+                                  unspecified edge_or_side_length, Done done, 
+                                  typename property_traits< WeightMap >::value_type spring_constant, 
+                                  VertexIndexMap index, 
+                                  DistanceMatrix distance, 
+                                  SpringStrengthMatrix spring_strength, 
+                                  PartialDerivativeMap partial_derivatives);
+template<typename Graph, typename PositionMap, typename WeightMap, typename T, 
+         bool EdgeOrSideLength, typename Done, typename VertexIndexMap> 
+  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position, 
+                                  WeightMap weight, 
+                                  unspecified edge_or_side_length, Done done, 
+                                  typename property_traits< WeightMap >::value_type spring_constant, 
+                                  VertexIndexMap index);
+template<typename Graph, typename PositionMap, typename WeightMap, typename T, 
+         bool EdgeOrSideLength, typename Done> 
+  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position, 
+                                  WeightMap weight, 
+                                  unspecified edge_or_side_length, Done done, 
+                                  typename property_traits< WeightMap >::value_type spring_constant = typename property_traits< WeightMap >::value_type(1));
+template<typename Graph, typename PositionMap, typename WeightMap, typename T, 
+         bool EdgeOrSideLength> 
+  bool kamada_kawai_spring_layout(const Graph & g, PositionMap position, 
+                                  WeightMap weight, 
+                                  unspecified edge_or_side_length);

Where Defined

boost/graph/kamada_kawai_spring_layout.hpp

Description

Parameters

distance

(UTIL/OUT) will be used to store the distance from every vertex to every other vertex, which is computed in the first stages of the algorithm. This value's type must be a model of BasicMatrix with value type equal to the value type of the weight map. The default is a a vector of vectors.

done

(IN) is a 4-argument function object that is passed the current value of delta_p (i.e., the energy of vertex p ), the vertex p , the graph g , and a boolean flag indicating whether delta_p is the maximum energy in the system (when true ) or the energy of the vertex being moved. Defaults to layout_tolerance instantiated over the value type of the weight map.

edge_or_side_length

(IN) provides either the unit length e of an edge in the layout or the length of a side s of the display area, and must be either boost::edge_length(e) or boost::side_length(s) , respectively.

g

(IN) must be a model of Vertex List Graph, Edge List Graph, and Incidence Graph and must be undirected.

index

(IN) is a mapping from vertices to index values between 0 and num_vertices(g) . The default is get(vertex_index,g) .

partial_derivatives

(UTIL) will be used to store the partial derivates of each vertex with respect to the x and y coordinates. This must be a Read/Write Property Map whose value type is a pair with both types equivalent to the value type of the weight map. The default is an iterator property map.

position

(OUT) must be a model of Lvalue Property Map, where the value type is a class containing fields x and y that will be set to the x and y coordinates of each vertex.

spring_constant

(IN) is the constant multiplied by each spring's strength. Larger values create systems with more energy that can take longer to stabilize; smaller values create systems with less energy that stabilize quickly but do not necessarily result in pleasing layouts. The default value is 1.

spring_strength

(UTIL/OUT) will be used to store the strength of the spring between every pair of vertices. This value's type must be a model of BasicMatrix with value type equal to the value type of the weight map. The default is a a vector of vectors.

weight

(IN) must be a model of Readable Property Map, which provides the weight of each edge in the graph g .

This algorithm [57] performs graph layout (in two dimensions) for connected, undirected graphs. It operates by relating the layout of graphs to a dynamic spring system and minimizing the energy within that system. The strength of a spring between two vertices is inversely proportional to the square of the shortest distance (in graph terms) between those two vertices. Essentially, vertices that are closer in the graph-theoretic sense (i.e., by following edges) will have stronger springs and will therefore be placed closer together.

Prior to invoking this algorithm, it is recommended that the vertices be placed along the vertices of a regular n-sided polygon via circle_layout.

Returns: + + true if layout was successful or false if a negative weight cycle was detected.


+ + + +
Copyright © 2004 +Douglas Gregor, Indiana University (dgregor@cs.indiana.edu)
+Andrew Lumsdaine, +Indiana University (lums@osl.iu.edu) +
+ + diff --git a/doc/layout_tolerance.html b/doc/layout_tolerance.html new file mode 100644 index 00000000..a931dd8d --- /dev/null +++ b/doc/layout_tolerance.html @@ -0,0 +1,14 @@ +Struct template layout_tolerance
c++boost.gif (8819 bytes)HomeLibrariesPeopleFAQMore

Struct template layout_tolerance

boost::layout_tolerance — Determines when to terminate layout of a particular graph based on a given tolerance.

Synopsis

template<typename T = double> 
+struct layout_tolerance {
+  // construct/copy/destruct
+  layout_tolerance(const T & = T(0.01));
+
+  // public member functions
+  template<typename Graph> 
+    bool operator()(T, 
+                    typename boost::graph_traits< Graph >::vertex_descriptor, 
+                    const Graph &, bool) ;
+};

Where Defined

boost/graph/kamada_kawai_spring_layout.hpp

Description

For local movements, where a single vertex is being moved toward a local minima the tolerance is taken as an absolute tolerance; for global movements, layout terminates when moving individual particles results in changes in the maximum vertex energy less than the tolerance.

layout_tolerance construct/copy/destruct

  1. layout_tolerance(const T & tolerance = T(0.01));

layout_tolerance public member functions

  1. template<typename Graph> 
    +  bool operator()(T delta_p, 
    +                  typename boost::graph_traits< Graph >::vertex_descriptor p, 
    +                  const Graph & g, bool global) ;

diff --git a/example/actor_clustering.cpp b/example/actor_clustering.cpp new file mode 100644 index 00000000..424c309e --- /dev/null +++ b/example/actor_clustering.cpp @@ -0,0 +1,187 @@ +// Copyright 2004 The Trustees of Indiana University. + +// Use, modification and distribution is subject to 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) + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// This program performs betweenness centrality (BC) clustering on the +// actor collaboration graph available at +// http://www.nd.edu/~networks/database/index.html and outputs the +// result of clustering in Pajek format. +// +// This program mimics the BC clustering algorithm program implemented +// by Shashikant Penumarthy for JUNG, so that we may compare results +// and timings. +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; + +struct Actor +{ + Actor(int id = -1) : id(id) {} + + int id; +}; + +typedef adjacency_list > ActorGraph; +typedef graph_traits::vertex_descriptor Vertex; +typedef graph_traits::edge_descriptor Edge; + +void load_actor_graph(std::istream& in, ActorGraph& g) +{ + std::map actors; + + std::string line; + while (getline(in, line)) { + std::vector actors_in_movie; + + // Map from the actor numbers on this line to the actor vertices + typedef tokenizer > Tok; + Tok tok(line, char_separator(" ")); + for (Tok::iterator id = tok.begin(); id != tok.end(); ++id) { + int actor_id = lexical_cast(*id); + std::map::iterator v = actors.find(actor_id); + if (v == actors.end()) { + Vertex new_vertex = add_vertex(Actor(actor_id), g); + actors[actor_id] = new_vertex; + actors_in_movie.push_back(new_vertex); + } else { + actors_in_movie.push_back(v->second); + } + } + + for (std::vector::iterator i = actors_in_movie.begin(); + i != actors_in_movie.end(); ++i) { + for (std::vector::iterator j = i + 1; + j != actors_in_movie.end(); ++j) { + if (!edge(*i, *j, g).second) add_edge(*i, *j, g); + } + } + } +} + +template +std::ostream& +write_pajek_graph(std::ostream& out, const Graph& g, + VertexIndexMap vertex_index, VertexNameMap vertex_name) +{ + out << "*Vertices " << num_vertices(g) << '\n'; + typedef typename graph_traits::vertex_iterator vertex_iterator; + for (vertex_iterator v = vertices(g).first; v != vertices(g).second; ++v) { + out << get(vertex_index, *v)+1 << " \"" << get(vertex_name, *v) << "\"\n"; + } + + out << "*Edges\n"; + typedef typename graph_traits::edge_iterator edge_iterator; + for (edge_iterator e = edges(g).first; e != edges(g).second; ++e) { + out << get(vertex_index, source(*e, g))+1 << ' ' + << get(vertex_index, target(*e, g))+1 << " 1.0\n"; // HACK! + } + return out; +} + +class actor_clustering_threshold : public bc_clustering_threshold +{ + typedef bc_clustering_threshold inherited; + + public: + actor_clustering_threshold(double threshold, const ActorGraph& g, + bool normalize) + : inherited(threshold, g, normalize), iter(1) { } + + bool operator()(double max_centrality, Edge e, const ActorGraph& g) + { + std::cout << "Iter: " << iter << " Max Centrality: " + << (max_centrality / dividend) << std::endl; + ++iter; + return inherited::operator()(max_centrality, e, g); + } + + private: + unsigned int iter; +}; + +int main(int argc, char* argv[]) +{ + std::string in_file; + std::string out_file; + double threshold = -1.0; + bool normalize = false; + + // Parse command-line options + { + int on_arg = 1; + while (on_arg < argc) { + std::string arg(argv[on_arg]); + if (arg == "-in") { + ++on_arg; assert(on_arg < argc); + in_file = argv[on_arg]; + } else if (arg == "-out") { + ++on_arg; assert(on_arg < argc); + out_file = argv[on_arg]; + } else if (arg == "-threshold") { + ++on_arg; assert(on_arg < argc); + threshold = lexical_cast(argv[on_arg]); + } else if (arg == "-normalize") { + normalize = true; + } else { + std::cerr << "Unrecognized parameter \"" << arg << "\".\n"; + return -1; + } + ++on_arg; + } + + if (in_file.empty() || out_file.empty() || threshold < 0) { + std::cerr << "error: syntax is actor_clustering [options]\n\n" + << "options are:\n" + << "\t-in \tInput file\n" + << "\t-out \tOutput file\n" + << "\t-threshold \tA threshold value\n" + << "\t-normalize\tNormalize edge centrality scores\n"; + return -1; + } + } + + ActorGraph g; + + // Load the actor graph + { + std::cout << "Building graph." << std::endl; + std::ifstream in(in_file.c_str()); + if (!in) { + std::cerr << "Unable to open file \"" << in_file << "\" for input.\n"; + return -2; + } + load_actor_graph(in, g); + } + + // Run the algorithm + std::cout << "Clusting..." << std::endl; + betweenness_centrality_clustering(g, + actor_clustering_threshold(threshold, g, normalize), + get(edge_centrality, g)); + + // Output the graph + { + std::cout << "Writing graph to file: " << out_file << std::endl; + std::ofstream out(out_file.c_str()); + if (!out) { + std::cerr << "Unable to open file \"" << out_file << "\" for output.\n"; + return -3; + } + write_pajek_graph(out, g, get(vertex_index, g), get(&Actor::id, g)); + } + return 0; +} diff --git a/include/boost/graph/betweenness_centrality_clustering.hpp b/include/boost/graph/betweenness_centrality_clustering.hpp new file mode 100644 index 00000000..5e668f9b --- /dev/null +++ b/include/boost/graph/betweenness_centrality_clustering.hpp @@ -0,0 +1,165 @@ +// Copyright 2004 The Trustees of Indiana University. + +// Use, modification and distribution is subject to 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) + +// Authors: Douglas Gregor +// Andrew Lumsdaine +#ifndef BOOST_GRAPH_BETWEENNESS_CENTRALITY_CLUSTERING_HPP +#define BOOST_GRAPH_BETWEENNESS_CENTRALITY_CLUSTERING_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { + +/** Threshold termination function for the betweenness centrality + * clustering algorithm. + */ +template +struct bc_clustering_threshold +{ + typedef T centrality_type; + + /// Terminate clustering when maximum absolute edge centrality is + /// below the given threshold. + explicit bc_clustering_threshold(T threshold) + : threshold(threshold), dividend(1.0) {} + + /** + * Terminate clustering when the maximum edge centrality is below + * the given threshold. + * + * @param threshold the threshold value + * + * @param g the graph on which the threshold will be calculated + * + * @param normalize when true, the threshold is compared against the + * normalized edge centrality based on the input graph; otherwise, + * the threshold is compared against the absolute edge centrality. + */ + template + bc_clustering_threshold(T threshold, const Graph& g, bool normalize = true) + : threshold(threshold), dividend(1.0) + { + if (normalize) { + typename graph_traits::vertices_size_type n = num_vertices(g); + dividend = T((n - 1) * (n - 2)) / T(2); + } + } + + /** Returns true when the given maximum edge centrality (potentially + * normalized) falls below the threshold. + */ + template + bool operator()(T max_centrality, Edge, const Graph&) + { + return (max_centrality / dividend) < threshold; + } + + protected: + T threshold; + T dividend; +}; + +/** Graph clustering based on edge betweenness centrality. + * + * This algorithm implements graph clustering based on edge + * betweenness centrality. It is an iterative algorithm, where in each + * step it compute the edge betweenness centrality (via @ref + * brandes_betweenness_centrality) and removes the edge with the + * maximum betweenness centrality. The @p done function object + * determines when the algorithm terminates (the edge found when the + * algorithm terminates will not be removed). + * + * @param g The graph on which clustering will be performed. The type + * of this parameter (@c MutableGraph) must be a model of the + * VertexListGraph, IncidenceGraph, EdgeListGraph, and Mutable Graph + * concepts. + * + * @param done The function object that indicates termination of the + * algorithm. It must be a ternary function object thats accepts the + * maximum centrality, the descriptor of the edge that will be + * removed, and the graph @p g. + * + * @param edge_centrality (UTIL/OUT) The property map that will store + * the betweenness centrality for each edge. When the algorithm + * terminates, it will contain the edge centralities for the + * graph. The type of this property map must model the + * ReadWritePropertyMap concept. Defaults to an @c + * iterator_property_map whose value type is + * @c Done::centrality_type and using @c get(edge_index, g) for the + * index map. + * + * @param vertex_index (IN) The property map that maps vertices to + * indices in the range @c [0, num_vertices(g)). This type of this + * property map must model the ReadablePropertyMap concept and its + * value type must be an integral type. Defaults to + * @c get(vertex_index, g). + */ +template +void +betweenness_centrality_clustering(MutableGraph& g, Done done, + EdgeCentralityMap edge_centrality, + VertexIndexMap vertex_index) +{ + typedef typename property_traits::value_type + centrality_type; + typedef typename graph_traits::edge_iterator edge_iterator; + typedef typename graph_traits::edge_descriptor edge_descriptor; + typedef typename graph_traits::vertices_size_type + vertices_size_type; + + if (edges(g).first == edges(g).second) return; + + // Function object that compares the centrality of edges + indirect_cmp > + cmp(edge_centrality); + + bool is_done; + do { + brandes_betweenness_centrality(g, + edge_centrality_map(edge_centrality) + .vertex_index_map(vertex_index)); + edge_descriptor e = *max_element(edges(g).first, edges(g).second, cmp); + centrality_type max_centrality = get(edge_centrality, e); + is_done = done(get(edge_centrality, e), e, g); + if (!is_done) remove_edge(e, g); + } while (!is_done && edges(g).first != edges(g).second); +} + +/** + * \overload + */ +template +void +betweenness_centrality_clustering(MutableGraph& g, Done done, + EdgeCentralityMap edge_centrality) +{ + betweenness_centrality_clustering(g, done, edge_centrality, + get(vertex_index, g)); +} + +/** + * \overload + */ +template +void +betweenness_centrality_clustering(MutableGraph& g, Done done) +{ + typedef typename Done::centrality_type centrality_type; + std::vector edge_centrality(num_edges(g)); + betweenness_centrality_clustering(g, done, + make_iterator_property_map(edge_centrality.begin(), get(edge_index, g)), + get(vertex_index, g)); +} + +} // end namespace boost + +#endif // BOOST_GRAPH_BETWEENNESS_CENTRALITY_CLUSTERING_HPP diff --git a/include/boost/graph/brandes_betweenness_centrality.hpp b/include/boost/graph/brandes_betweenness_centrality.hpp new file mode 100644 index 00000000..6cf7ecec --- /dev/null +++ b/include/boost/graph/brandes_betweenness_centrality.hpp @@ -0,0 +1,599 @@ +// Copyright 2004 The Trustees of Indiana University. + +// Use, modification and distribution is subject to 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) + +// Authors: Douglas Gregor +// Andrew Lumsdaine +#ifndef BOOST_GRAPH_BRANDES_BETWEENNESS_CENTRALITY_HPP +#define BOOST_GRAPH_BRANDES_BETWEENNESS_CENTRALITY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + +namespace detail { namespace graph { + + /** + * Customized visitor passed to Dijkstra's algorithm by Brandes' + * betweenness centrality algorithm. This visitor is responsible for + * keeping track of the order in which vertices are discovered, the + * predecessors on the shortest path(s) to a vertex, and the number + * of shortest paths. + */ + template + struct brandes_dijkstra_visitor : public bfs_visitor<> + { + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + typedef typename graph_traits::edge_descriptor edge_descriptor; + + brandes_dijkstra_visitor(std::stack& ordered_vertices, + WeightMap weight, + IncomingMap incoming, + DistanceMap distance, + PathCountMap path_count) + : ordered_vertices(ordered_vertices), weight(weight), + incoming(incoming), distance(distance), + path_count(path_count) + { } + + /** + * Whenever an edge e = (v, w) is relaxed, the incoming edge list + * for w is set to {(v, w)} and the shortest path count of w is set to + * the number of paths that reach {v}. + */ + void edge_relaxed(edge_descriptor e, const Graph& g) + { + vertex_descriptor v = source(e, g), w = target(e, g); + incoming[w].clear(); + incoming[w].push_back(e); + put(path_count, w, get(path_count, v)); + } + + /** + * If an edge e = (v, w) was not relaxed, it may still be the case + * that we've found more equally-short paths, so include {(v, w)} in the + * incoming edges of w and add all of the shortest paths to v to the + * shortest path count of w. + */ + void edge_not_relaxed(edge_descriptor e, const Graph& g) + { + typedef typename property_traits::value_type weight_type; + typedef typename property_traits::value_type distance_type; + vertex_descriptor v = source(e, g), w = target(e, g); + distance_type d_v = get(distance, v), d_w = get(distance, w); + weight_type w_e = get(weight, e); + + closed_plus combine; + if (d_w == combine(d_v, w_e)) { + put(path_count, w, get(path_count, w) + get(path_count, v)); + incoming[w].push_back(e); + } + } + + /// Keep track of vertices as they are reached + void examine_vertex(vertex_descriptor w, const Graph&) + { + ordered_vertices.push(w); + } + + private: + std::stack& ordered_vertices; + WeightMap weight; + IncomingMap incoming; + DistanceMap distance; + PathCountMap path_count; + }; + + /** + * Function object that calls Dijkstra's shortest paths algorithm + * using the Dijkstra visitor for the Brandes betweenness centrality + * algorithm. + */ + template + struct brandes_dijkstra_shortest_paths + { + brandes_dijkstra_shortest_paths(WeightMap weight_map) + : weight_map(weight_map) { } + + template + void + operator()(Graph& g, + typename graph_traits::vertex_descriptor s, + std::stack::vertex_descriptor>& ov, + IncomingMap incoming, + DistanceMap distance, + PathCountMap path_count, + VertexIndexMap vertex_index) + { + typedef brandes_dijkstra_visitor visitor_type; + visitor_type visitor(ov, weight_map, incoming, distance, path_count); + + dijkstra_shortest_paths(g, s, + boost::weight_map(weight_map) + .vertex_index_map(vertex_index) + .distance_map(distance) + .visitor(visitor)); + } + + private: + WeightMap weight_map; + }; + + /** + * Function object that invokes breadth-first search for the + * unweighted form of the Brandes betweenness centrality algorithm. + */ + struct brandes_unweighted_shortest_paths + { + /** + * Customized visitor passed to breadth-first search, which + * records predecessor and the number of shortest paths to each + * vertex. + */ + template + struct visitor_type : public bfs_visitor<> + { + typedef typename graph_traits::edge_descriptor edge_descriptor; + typedef typename graph_traits::vertex_descriptor + vertex_descriptor; + + visitor_type(IncomingMap incoming, DistanceMap distance, + PathCountMap path_count, + std::stack& ordered_vertices) + : incoming(incoming), distance(distance), + path_count(path_count), ordered_vertices(ordered_vertices) { } + + /// Keep track of vertices as they are reached + void examine_vertex(vertex_descriptor v, Graph&) + { + ordered_vertices.push(v); + } + + /** + * Whenever an edge e = (v, w) is labelled a tree edge, the + * incoming edge list for w is set to {(v, w)} and the shortest + * path count of w is set to the number of paths that reach {v}. + */ + void tree_edge(edge_descriptor e, Graph& g) + { + vertex_descriptor v = source(e, g); + vertex_descriptor w = target(e, g); + put(distance, w, get(distance, v) + 1); + + put(path_count, w, get(path_count, v)); + incoming[w].push_back(e); + } + + /** + * If an edge e = (v, w) is not a tree edge, it may still be the + * case that we've found more equally-short paths, so include (v, w) + * in the incoming edge list of w and add all of the shortest + * paths to v to the shortest path count of w. + */ + void non_tree_edge(edge_descriptor e, Graph& g) + { + vertex_descriptor v = source(e, g); + vertex_descriptor w = target(e, g); + if (get(distance, w) == get(distance, v) + 1) { + put(path_count, w, get(path_count, w) + get(path_count, v)); + incoming[w].push_back(e); + } + } + + private: + IncomingMap incoming; + DistanceMap distance; + PathCountMap path_count; + std::stack& ordered_vertices; + }; + + template + void + operator()(Graph& g, + typename graph_traits::vertex_descriptor s, + std::stack::vertex_descriptor>& ov, + IncomingMap incoming, + DistanceMap distance, + PathCountMap path_count, + VertexIndexMap vertex_index) + { + typedef typename graph_traits::vertex_descriptor + vertex_descriptor; + + visitor_type + visitor(incoming, distance, path_count, ov); + + std::vector + colors(num_vertices(g), color_traits::white()); + boost::queue Q; + breadth_first_visit(g, s, Q, visitor, + make_iterator_property_map(colors.begin(), + vertex_index)); + } + }; + + // When the edge centrality map is a dummy property map, no + // initialization is needed. + template + inline void + init_centrality_map(std::pair, dummy_property_map) { } + + // When we have a real edge centrality map, initialize all of the + // centralities to zero. + template + void + init_centrality_map(std::pair keys, Centrality centrality_map) + { + typedef typename property_traits::value_type + centrality_type; + while (keys.first != keys.second) { + put(centrality_map, *keys.first, centrality_type(0)); + ++keys.first; + } + } + + // When the edge centrality map is a dummy property map, no update + // is performed. + template + inline void + update_centrality(dummy_property_map, const Key&, const T&) { } + + // When we have a real edge centrality map, add the value to the map + template + inline void + update_centrality(CentralityMap centrality_map, Key k, const T& x) + { put(centrality_map, k, get(centrality_map, k) + x); } + + template + inline void + divide_centrality_by_two(std::pair, dummy_property_map) {} + + template + inline void + divide_centrality_by_two(std::pair keys, + CentralityMap centrality_map) + { + typename property_traits::value_type two(2); + while (keys.first != keys.second) { + put(centrality_map, *keys.first, get(centrality_map, *keys.first) / two); + ++keys.first; + } + } + + template + void + brandes_betweenness_centrality_impl(const Graph& g, + CentralityMap centrality, // C_B + EdgeCentralityMap edge_centrality_map, + IncomingMap incoming, // P + DistanceMap distance, // d + DependencyMap dependency, // delta + PathCountMap path_count, // sigma + VertexIndexMap vertex_index, + ShortestPaths shortest_paths) + { + typedef typename graph_traits::vertex_iterator vertex_iterator; + typedef typename graph_traits::edge_iterator edge_iterator; + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + + // Initialize centrality + init_centrality_map(vertices(g), centrality); + init_centrality_map(edges(g), edge_centrality_map); + + std::stack ordered_vertices; + vertex_iterator s, s_end; + for (tie(s, s_end) = vertices(g); s != s_end; ++s) { + // Initialize for this iteration + vertex_iterator w, w_end; + for (tie(w, w_end) = vertices(g); w != w_end; ++w) { + incoming[*w].clear(); + put(path_count, *w, 0); + put(dependency, *w, 0); + } + put(path_count, *s, 1); + + // Execute the shortest paths algorithm. This will be either + // Dijkstra's algorithm or a customized breadth-first search, + // depending on whether the graph is weighted or unweighted. + shortest_paths(g, *s, ordered_vertices, incoming, distance, + path_count, vertex_index); + + while (!ordered_vertices.empty()) { + vertex_descriptor w = ordered_vertices.top(); + ordered_vertices.pop(); + + typedef typename property_traits::value_type + incoming_type; + typedef typename incoming_type::iterator incoming_iterator; + typedef typename property_traits::value_type + dependency_type; + + for (incoming_iterator vw = incoming[w].begin(); + vw != incoming[w].end(); ++vw) { + vertex_descriptor v = source(*vw, g); + dependency_type factor = dependency_type(get(path_count, v)) + / dependency_type(get(path_count, w)); + factor *= (dependency_type(1) + get(dependency, w)); + put(dependency, v, get(dependency, v) + factor); + update_centrality(edge_centrality_map, *vw, factor); + } + + if (w != *s) { + update_centrality(centrality, w, get(dependency, w)); + } + } + } + + typedef typename graph_traits::directed_category directed_category; + const bool is_undirected = + is_convertible::value; + if (is_undirected) { + divide_centrality_by_two(vertices(g), centrality); + divide_centrality_by_two(edges(g), edge_centrality_map); + } + } + +} } // end namespace detail::graph + +template +void +brandes_betweenness_centrality(const Graph& g, + CentralityMap centrality, // C_B + EdgeCentralityMap edge_centrality_map, + IncomingMap incoming, // P + DistanceMap distance, // d + DependencyMap dependency, // delta + PathCountMap path_count, // sigma + VertexIndexMap vertex_index) +{ + detail::graph::brandes_unweighted_shortest_paths shortest_paths; + + detail::graph::brandes_betweenness_centrality_impl(g, centrality, + edge_centrality_map, + incoming, distance, + dependency, path_count, + vertex_index, + shortest_paths); +} + +template +void +brandes_betweenness_centrality(const Graph& g, + CentralityMap centrality, // C_B + EdgeCentralityMap edge_centrality_map, + IncomingMap incoming, // P + DistanceMap distance, // d + DependencyMap dependency, // delta + PathCountMap path_count, // sigma + VertexIndexMap vertex_index, + WeightMap weight_map) +{ + detail::graph::brandes_dijkstra_shortest_paths + shortest_paths(weight_map); + + detail::graph::brandes_betweenness_centrality_impl(g, centrality, + edge_centrality_map, + incoming, distance, + dependency, path_count, + vertex_index, + shortest_paths); +} + +namespace detail { namespace graph { + template + void + brandes_betweenness_centrality_dispatch2(const Graph& g, + CentralityMap centrality, + EdgeCentralityMap edge_centrality_map, + WeightMap weight_map, + VertexIndexMap vertex_index) + { + typedef typename graph_traits::degree_size_type degree_size_type; + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + typedef typename graph_traits::edge_descriptor edge_descriptor; + typedef typename mpl::if_c<(is_same::value), + EdgeCentralityMap, + CentralityMap>::type a_centrality_map; + typedef typename property_traits::value_type + centrality_type; + + typename graph_traits::vertices_size_type V = num_vertices(g); + + std::vector > incoming(V); + std::vector distance(V); + std::vector dependency(V); + std::vector path_count(V); + + brandes_betweenness_centrality( + g, centrality, edge_centrality_map, + make_iterator_property_map(incoming.begin(), vertex_index), + make_iterator_property_map(distance.begin(), vertex_index), + make_iterator_property_map(dependency.begin(), vertex_index), + make_iterator_property_map(path_count.begin(), vertex_index), + vertex_index, + weight_map); + } + + + template + void + brandes_betweenness_centrality_dispatch2(const Graph& g, + CentralityMap centrality, + EdgeCentralityMap edge_centrality_map, + VertexIndexMap vertex_index) + { + typedef typename graph_traits::degree_size_type degree_size_type; + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + typedef typename graph_traits::edge_descriptor edge_descriptor; + typedef typename mpl::if_c<(is_same::value), + EdgeCentralityMap, + CentralityMap>::type a_centrality_map; + typedef typename property_traits::value_type + centrality_type; + + typename graph_traits::vertices_size_type V = num_vertices(g); + + std::vector > incoming(V); + std::vector distance(V); + std::vector dependency(V); + std::vector path_count(V); + + brandes_betweenness_centrality( + g, centrality, edge_centrality_map, + make_iterator_property_map(incoming.begin(), vertex_index), + make_iterator_property_map(distance.begin(), vertex_index), + make_iterator_property_map(dependency.begin(), vertex_index), + make_iterator_property_map(path_count.begin(), vertex_index), + vertex_index); + } + + template + struct brandes_betweenness_centrality_dispatch1 + { + template + static void + run(const Graph& g, CentralityMap centrality, + EdgeCentralityMap edge_centrality_map, VertexIndexMap vertex_index, + WeightMap weight_map) + { + brandes_betweenness_centrality_dispatch2(g, centrality, edge_centrality_map, + weight_map, vertex_index); + } + }; + + template<> + struct brandes_betweenness_centrality_dispatch1 + { + template + static void + run(const Graph& g, CentralityMap centrality, + EdgeCentralityMap edge_centrality_map, VertexIndexMap vertex_index, + error_property_not_found) + { + brandes_betweenness_centrality_dispatch2(g, centrality, edge_centrality_map, + vertex_index); + } + }; + +} } // end namespace detail::graph + +template +void +brandes_betweenness_centrality(const Graph& g, + const bgl_named_params& params) +{ + typedef bgl_named_params named_params; + + typedef typename property_value::type ew; + detail::graph::brandes_betweenness_centrality_dispatch1::run( + g, + choose_param(get_param(params, vertex_centrality), + dummy_property_map()), + choose_param(get_param(params, edge_centrality), + dummy_property_map()), + choose_const_pmap(get_param(params, vertex_index), g, vertex_index), + get_param(params, edge_weight)); +} + +template +void +brandes_betweenness_centrality(const Graph& g, CentralityMap centrality) +{ + detail::graph::brandes_betweenness_centrality_dispatch2( + g, centrality, dummy_property_map(), get(vertex_index, g)); +} + +template +void +brandes_betweenness_centrality(const Graph& g, CentralityMap centrality, + EdgeCentralityMap edge_centrality_map) +{ + detail::graph::brandes_betweenness_centrality_dispatch2( + g, centrality, edge_centrality_map, get(vertex_index, g)); +} + +/** + * Converts "absolute" betweenness centrality (as computed by the + * brandes_betweenness_centrality algorithm) in the centrality map + * into "relative" centrality. The result is placed back into the + * given centrality map. + */ +template +void +relative_betweenness_centrality(const Graph& g, CentralityMap centrality) +{ + typedef typename graph_traits::vertex_iterator vertex_iterator; + typedef typename property_traits::value_type centrality_type; + + typename graph_traits::vertices_size_type n = num_vertices(g); + centrality_type factor = centrality_type(2)/centrality_type(n*n - 3*n + 2); + vertex_iterator v, v_end; + for (tie(v, v_end) = vertices(g); v != v_end; ++v) { + put(centrality, *v, factor * get(centrality, *v)); + } +} + +// Compute the central point dominance of a graph. +template +typename property_traits::value_type +central_point_dominance(const Graph& g, CentralityMap centrality) +{ + using std::max; + + typedef typename graph_traits::vertex_iterator vertex_iterator; + typedef typename property_traits::value_type centrality_type; + + typename graph_traits::vertices_size_type n = num_vertices(g); + + // Find max centrality + centrality_type max_centrality(0); + vertex_iterator v, v_end; + for (tie(v, v_end) = vertices(g); v != v_end; ++v) { + max_centrality = max(max_centrality, get(centrality, *v)); + } + + // Compute central point dominance + centrality_type sum(0); + for (tie(v, v_end) = vertices(g); v != v_end; ++v) { + sum += (max_centrality - get(centrality, *v)); + } + return sum/(n-1); +} + +} // end namespace boost + +#endif // BOOST_GRAPH_BRANDES_BETWEENNESS_CENTRALITY_HPP diff --git a/include/boost/graph/circle_layout.hpp b/include/boost/graph/circle_layout.hpp new file mode 100644 index 00000000..4803750d --- /dev/null +++ b/include/boost/graph/circle_layout.hpp @@ -0,0 +1,53 @@ +// Copyright 2004 The Trustees of Indiana University. + +// Use, modification and distribution is subject to 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) + +// Authors: Douglas Gregor +// Andrew Lumsdaine +#ifndef BOOST_GRAPH_CIRCLE_LAYOUT_HPP +#define BOOST_GRAPH_CIRCLE_LAYOUT_HPP +#include +#include +#include + +namespace boost { + /** + * \brief Layout the graph with the vertices at the points of a regular + * n-polygon. + * + * The distance from the center of the polygon to each point is + * determined by the @p radius parameter. The @p position parameter + * must be an Lvalue Property Map whose value type is a class type + * containing @c x and @c y members that will be set to the @c x and + * @c y coordinates. + */ + template + void + circle_graph_layout(const VertexListGraph& g, PositionMap position, + Radius radius) + { + const double pi = 3.14159; + + using std::sin; + using std::cos; + + typedef typename graph_traits::vertices_size_type + vertices_size_type; + + vertices_size_type n = num_vertices(g); + + typedef typename graph_traits::vertex_iterator + vertex_iterator; + + vertices_size_type i = 0; + for(std::pair v = vertices(g); + v.first != v.second; ++v.first, ++i) { + position[*v.first].x = radius * cos(i * 2 * pi / n); + position[*v.first].y = radius * sin(i * 2 * pi / n); + } + } +} // end namespace boost + +#endif // BOOST_GRAPH_CIRCLE_LAYOUT_HPP diff --git a/include/boost/graph/kamada_kawai_spring_layout.hpp b/include/boost/graph/kamada_kawai_spring_layout.hpp new file mode 100644 index 00000000..d486dbeb --- /dev/null +++ b/include/boost/graph/kamada_kawai_spring_layout.hpp @@ -0,0 +1,506 @@ +// Copyright 2004 The Trustees of Indiana University. + +// Use, modification and distribution is subject to 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) + +// Authors: Douglas Gregor +// Andrew Lumsdaine +#ifndef BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP +#define BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + namespace detail { namespace graph { + /** + * Denotes an edge or display area side length used to scale a + * Kamada-Kawai drawing. + */ + template + struct edge_or_side + { + explicit edge_or_side(T value) : value(value) {} + + T value; + }; + + /** + * Compute the edge length from an edge length. This is trivial. + */ + template + T compute_edge_length(const Graph&, DistanceMap, IndexMap, + edge_or_side length) + { return length.value; } + + /** + * Compute the edge length based on the display area side + length. We do this by dividing the side length by the largest + shortest distance between any two vertices in the graph. + */ + template + T + compute_edge_length(const Graph& g, DistanceMap distance, IndexMap index, + edge_or_side length) + { + T result(0); + + typedef typename graph_traits::vertex_iterator vertex_iterator; + + for (vertex_iterator ui = vertices(g).first, end = vertices(g).second; + ui != end; ++ui) { + vertex_iterator vi = ui; + for (++vi; vi != end; ++vi) { + T dij = distance[get(index, *ui)][get(index, *vi)]; + if (dij > result) result = dij; + } + } + return length.value / result; + } + + /** + * Implementation of the Kamada-Kawai spring layout algorithm. + */ + template + struct kamada_kawai_spring_layout_impl + { + typedef typename property_traits::value_type weight_type; + typedef std::pair deriv_type; + typedef typename graph_traits::vertex_iterator vertex_iterator; + typedef typename graph_traits::vertex_descriptor + vertex_descriptor; + + kamada_kawai_spring_layout_impl( + const Graph& g, + PositionMap position, + WeightMap weight, + EdgeOrSideLength edge_or_side_length, + Done done, + weight_type spring_constant, + VertexIndexMap index, + DistanceMatrix distance, + SpringStrengthMatrix spring_strength, + PartialDerivativeMap partial_derivatives) + : g(g), position(position), weight(weight), + edge_or_side_length(edge_or_side_length), done(done), + spring_constant(spring_constant), index(index), distance(distance), + spring_strength(spring_strength), + partial_derivatives(partial_derivatives) {} + + // Compute contribution of vertex i to the first partial + // derivatives (dE/dx_m, dE/dy_m) (for vertex m) + deriv_type + compute_partial_derivative(vertex_descriptor m, vertex_descriptor i) + { + using std::sqrt; + + deriv_type result(0, 0); + if (i != m) { + weight_type x_diff = position[m].x - position[i].x; + weight_type y_diff = position[m].y - position[i].y; + weight_type dist = sqrt(x_diff * x_diff + y_diff * y_diff); + result.first = spring_strength[get(index, m)][get(index, i)] + * (x_diff - distance[get(index, m)][get(index, i)]*x_diff/dist); + result.second = spring_strength[get(index, m)][get(index, i)] + * (y_diff - distance[get(index, m)][get(index, i)]*y_diff/dist); + } + + return result; + } + + // Compute partial derivatives dE/dx_m and dE/dy_m + deriv_type + compute_partial_derivatives(vertex_descriptor m) + { + using std::sqrt; + + deriv_type result(0, 0); + + // TBD: looks like an accumulate to me + std::pair verts = vertices(g); + for (/* no init */; verts.first != verts.second; ++verts.first) { + vertex_descriptor i = *verts.first; + deriv_type deriv = compute_partial_derivative(m, i); + result.first += deriv.first; + result.second += deriv.second; + } + + return result; + } + + // The actual Kamada-Kawai spring layout algorithm implementation + bool run() + { + using std::sqrt; + + // Compute d_{ij} and place it in the distance matrix + if (!johnson_all_pairs_shortest_paths(g, distance, index, weight, + weight_type(0))) + return false; + + // Compute L based on side length (if needed), or retrieve L + weight_type edge_length = + detail::graph::compute_edge_length(g, distance, index, + edge_or_side_length); + + // Compute l_{ij} and k_{ij} + const weight_type K = spring_constant; + vertex_iterator ui, end = vertices(g).second; + for (ui = vertices(g).first; ui != end; ++ui) { + vertex_iterator vi = ui; + for (++vi; vi != end; ++vi) { + weight_type dij = distance[get(index, *ui)][get(index, *vi)]; + distance[get(index, *ui)][get(index, *vi)] = edge_length * dij; + distance[get(index, *vi)][get(index, *ui)] = edge_length * dij; + spring_strength[get(index, *ui)][get(index, *vi)] = K/(dij*dij); + spring_strength[get(index, *vi)][get(index, *ui)] = K/(dij*dij); + } + } + + // Compute Delta_i and find max + vertex_descriptor p = *vertices(g).first; + weight_type delta_p(0); + + for (ui = vertices(g).first; ui != end; ++ui) { + deriv_type deriv = compute_partial_derivatives(*ui); + put(partial_derivatives, *ui, deriv); + + weight_type delta = + sqrt(deriv.first*deriv.first + deriv.second*deriv.second); + + if (delta > delta_p) { + p = *ui; + delta_p = delta; + } + } + + while (!done(delta_p, p, g, true)) { + // The contribution p makes to the partial derivatives of + // each vertex. Computing this (at O(n) cost) allows us to + // update the delta_i values in O(n) time instead of O(n^2) + // time. + std::vector p_partials(num_vertices(g)); + for (ui = vertices(g).first; ui != end; ++ui) { + vertex_descriptor i = *ui; + p_partials[get(index, i)] = compute_partial_derivative(i, p); + } + + do { + // Compute the 4 elements of the Jacobian + weight_type dE_dx_dx = 0, dE_dx_dy = 0, dE_dy_dx = 0, dE_dy_dy = 0; + for (ui = vertices(g).first; ui != end; ++ui) { + vertex_descriptor i = *ui; + if (i != p) { + weight_type x_diff = position[p].x - position[i].x; + weight_type y_diff = position[p].y - position[i].y; + weight_type dist = sqrt(x_diff * x_diff + y_diff * y_diff); + weight_type dist_cubed = dist * dist * dist; + weight_type k_mi = spring_strength[get(index,p)][get(index,i)]; + weight_type l_mi = distance[get(index, p)][get(index, i)]; + dE_dx_dx += k_mi * (1 - (l_mi * y_diff * y_diff)/dist_cubed); + dE_dx_dy += k_mi * l_mi * x_diff * y_diff / dist_cubed; + dE_dy_dx += k_mi * l_mi * x_diff * y_diff / dist_cubed; + dE_dy_dy += k_mi * (1 - (l_mi * x_diff * x_diff)/dist_cubed); + } + } + + // Solve for delta_x and delta_y + weight_type dE_dx = get(partial_derivatives, p).first; + weight_type dE_dy = get(partial_derivatives, p).second; + + weight_type delta_x = + (dE_dx_dy * dE_dy - dE_dy_dy * dE_dx) + / (dE_dx_dx * dE_dy_dy - dE_dx_dy * dE_dy_dx); + + weight_type delta_y = + (dE_dx_dx * dE_dy - dE_dy_dx * dE_dx) + / (dE_dy_dx * dE_dx_dy - dE_dx_dx * dE_dy_dy); + + + // Move p by (delta_x, delta_y) + position[p].x += delta_x; + position[p].y += delta_y; + + // Recompute partial derivatives and delta_p + deriv_type deriv = compute_partial_derivatives(p); + put(partial_derivatives, p, deriv); + + delta_p = + sqrt(deriv.first*deriv.first + deriv.second*deriv.second); + } while (!done(delta_p, p, g, false)); + + // Select new p by updating each partial derivative and delta + vertex_descriptor old_p; + for (ui = vertices(g).first; ui != end; ++ui) { + deriv_type old_deriv_p = p_partials[get(index, *ui)]; + deriv_type old_p_partial = + compute_partial_derivative(*ui, old_p); + deriv_type deriv = get(partial_derivatives, *ui); + + deriv.first += old_p_partial.first - old_deriv_p.first; + deriv.second += old_p_partial.second - old_deriv_p.second; + + put(partial_derivatives, *ui, deriv); + weight_type delta = + sqrt(deriv.first*deriv.first + deriv.second*deriv.second); + + if (delta > delta_p) { + p = *ui; + delta_p = delta; + } + } + } + + return true; + } + + const Graph& g; + PositionMap position; + WeightMap weight; + EdgeOrSideLength edge_or_side_length; + Done done; + weight_type spring_constant; + VertexIndexMap index; + DistanceMatrix distance; + SpringStrengthMatrix spring_strength; + PartialDerivativeMap partial_derivatives; + }; + } } // end namespace detail::graph + + /// States that the given quantity is an edge length. + template + inline detail::graph::edge_or_side + edge_length(T x) + { return detail::graph::edge_or_side(x); } + + /// States that the given quantity is a display area side length. + template + inline detail::graph::edge_or_side + side_length(T x) + { return detail::graph::edge_or_side(x); } + + /** + * \brief Determines when to terminate layout of a particular graph based + * on a given tolerance. + * + * For local movements, where a single vertex is being moved toward + * a local minima the tolerance is taken as an absolute tolerance; + * for global movements, layout terminates when moving individual + * particles results in changes in the maximum vertex energy less + * than the tolerance. + */ + template + struct layout_tolerance + { + layout_tolerance(const T& tolerance = T(0.01)) + : tolerance(tolerance), last_energy(std::numeric_limits::max()) { } + + template + bool + operator()(T delta_p, + typename boost::graph_traits::vertex_descriptor p, + const Graph& g, + bool global) + { + if (global) { + double diff = fabs(last_energy - delta_p); + if (diff < T(0)) diff = -diff; + last_energy = delta_p; + return diff < tolerance; + } else { + return delta_p < tolerance; + } + } + + private: + T tolerance; + T last_energy; + }; + + /** \brief Kamada-Kawai spring layout for undirected graphs. + * + * This algorithm performs graph layout (in two dimensions) for + * connected, undirected graphs. It operates by relating the layout + * of graphs to a dynamic spring system and minimizing the energy + * within that system. The strength of a spring between two vertices + * is inversely proportional to the square of the shortest distance + * (in graph terms) between those two vertices. Essentially, + * vertices that are closer in the graph-theoretic sense (i.e., by + * following edges) will have stronger springs and will therefore be + * placed closer together. + * + * Prior to invoking this algorithm, it is recommended that the + * vertices be placed along the vertices of a regular n-sided + * polygon. + * + * \param g (IN) must be a model of Vertex List Graph, Edge List + * Graph, and Incidence Graph and must be undirected. + * + * \param position (OUT) must be a model of Lvalue Property Map, + * where the value type is a class containing fields @c x and @c y + * that will be set to the @c x and @c y coordinates of each vertex. + * + * \param weight (IN) must be a model of Readable Property Map, + * which provides the weight of each edge in the graph @p g. + * + * \param edge_or_side_length (IN) provides either the unit length + * @c e of an edge in the layout or the length of a side @c s of the + * display area, and must be either @c boost::edge_length(e) or @c + * boost::side_length(s), respectively. + * + * \param done (IN) is a 4-argument function object that is passed + * the current value of delta_p (i.e., the energy of vertex @p p), + * the vertex @p p, the graph @p g, and a boolean flag indicating + * whether @p delta_p is the maximum energy in the system (when @c + * true) or the energy of the vertex being moved. Defaults to @c + * layout_tolerance instantiated over the value type of the weight + * map. + * + * \param spring_constant (IN) is the constant multiplied by each + * spring's strength. Larger values create systems with more energy + * that can take longer to stabilize; smaller values create systems + * with less energy that stabilize quickly but do not necessarily + * result in pleasing layouts. The default value is 1. + * + * \param index (IN) is a mapping from vertices to index values + * between 0 and @c num_vertices(g). The default is @c + * get(vertex_index,g). + * + * \param distance (UTIL/OUT) will be used to store the distance + * from every vertex to every other vertex, which is computed in the + * first stages of the algorithm. This value's type must be a model + * of BasicMatrix with value type equal to the value type of the + * weight map. The default is a a vector of vectors. + * + * \param spring_strength (UTIL/OUT) will be used to store the + * strength of the spring between every pair of vertices. This + * value's type must be a model of BasicMatrix with value type equal + * to the value type of the weight map. The default is a a vector of + * vectors. + * + * \param partial_derivatives (UTIL) will be used to store the + * partial derivates of each vertex with respect to the @c x and @c + * y coordinates. This must be a Read/Write Property Map whose value + * type is a pair with both types equivalent to the value type of + * the weight map. The default is an iterator property map. + * + * \returns @c true if layout was successful or @c false if a + * negative weight cycle was detected. + */ + template + bool + kamada_kawai_spring_layout( + const Graph& g, + PositionMap position, + WeightMap weight, + detail::graph::edge_or_side edge_or_side_length, + Done done, + typename property_traits::value_type spring_constant, + VertexIndexMap index, + DistanceMatrix distance, + SpringStrengthMatrix spring_strength, + PartialDerivativeMap partial_derivatives) + { + BOOST_STATIC_ASSERT((is_convertible< + typename graph_traits::directed_category*, + undirected_tag* + >::value)); + + detail::graph::kamada_kawai_spring_layout_impl< + Graph, PositionMap, WeightMap, + detail::graph::edge_or_side, Done, VertexIndexMap, + DistanceMatrix, SpringStrengthMatrix, PartialDerivativeMap> + alg(g, position, weight, edge_or_side_length, done, spring_constant, + index, distance, spring_strength, partial_derivatives); + return alg.run(); + } + + /** + * \overload + */ + template + bool + kamada_kawai_spring_layout( + const Graph& g, + PositionMap position, + WeightMap weight, + detail::graph::edge_or_side edge_or_side_length, + Done done, + typename property_traits::value_type spring_constant, + VertexIndexMap index) + { + typedef typename property_traits::value_type weight_type; + + typename graph_traits::vertices_size_type n = num_vertices(g); + typedef std::vector weight_vec; + + std::vector distance(n, weight_vec(n)); + std::vector spring_strength(n, weight_vec(n)); + std::vector > partial_derivatives(n); + + return + kamada_kawai_spring_layout( + g, position, weight, edge_or_side_length, done, spring_constant, index, + distance.begin(), + spring_strength.begin(), + make_iterator_property_map(partial_derivatives.begin(), index)); + } + + /** + * \overload + */ + template + bool + kamada_kawai_spring_layout( + const Graph& g, + PositionMap position, + WeightMap weight, + detail::graph::edge_or_side edge_or_side_length, + Done done, + typename property_traits::value_type spring_constant = + typename property_traits::value_type(1)) + { + return kamada_kawai_spring_layout(g, position, weight, edge_or_side_length, + done, spring_constant, + get(vertex_index, g)); + } + + /** + * \overload + */ + template + bool + kamada_kawai_spring_layout( + const Graph& g, + PositionMap position, + WeightMap weight, + detail::graph::edge_or_side edge_or_side_length) + { + typedef typename property_traits::value_type weight_type; + return kamada_kawai_spring_layout(g, position, weight, edge_or_side_length, + layout_tolerance(), + weight_type(1.0), + get(vertex_index, g)); + } +} // end namespace boost + +#endif // BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP diff --git a/test/brandes_betweenness_centrality_test.cpp b/test/brandes_betweenness_centrality_test.cpp new file mode 100644 index 00000000..464c2130 --- /dev/null +++ b/test/brandes_betweenness_centrality_test.cpp @@ -0,0 +1,479 @@ +// Copyright 2004 The Trustees of Indiana University. + +// Use, modification and distribution is subject to 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) + +// Authors: Douglas Gregor +// Andrew Lumsdaine +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; + +const double error_tolerance = 0.001; + +typedef property > EdgeProperties; + +struct weighted_edge +{ + int source, target; + double weight; +}; + +template +void +run_weighted_test(Graph*, int V, weighted_edge edge_init[], int E, + double correct_centrality[]) +{ + Graph g(V); + typedef typename graph_traits::vertex_descriptor Vertex; + typedef typename graph_traits::vertex_iterator vertex_iterator; + typedef typename graph_traits::edge_descriptor Edge; + + std::vector vertices(V); + { + vertex_iterator v, v_end; + int index = 0; + for (tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) { + put(vertex_index, g, *v, index); + vertices[index] = *v; + } + } + + std::vector edges(E); + for (int e = 0; e < E; ++e) { + edges[e] = add_edge(vertices[edge_init[e].source], + vertices[edge_init[e].target], + g).first; + put(edge_weight, g, edges[e], 1.0); + } + + std::vector centrality(V); + brandes_betweenness_centrality( + g, + centrality_map( + make_iterator_property_map(centrality.begin(), get(vertex_index, g))) + .vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g))); + + + for (int v = 0; v < V; ++v) { + BOOST_TEST(centrality[v] == correct_centrality[v]); + } +} + +struct unweighted_edge +{ + int source, target; +}; + +template +void +run_unweighted_test(Graph*, int V, unweighted_edge edge_init[], int E, + double correct_centrality[], + double* correct_edge_centrality = 0) +{ + Graph g(V); + typedef typename graph_traits::vertex_descriptor Vertex; + typedef typename graph_traits::vertex_iterator vertex_iterator; + typedef typename graph_traits::edge_descriptor Edge; + + std::vector vertices(V); + { + vertex_iterator v, v_end; + int index = 0; + for (tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) { + put(vertex_index, g, *v, index); + vertices[index] = *v; + } + } + + std::vector edges(E); + for (int e = 0; e < E; ++e) { + edges[e] = add_edge(vertices[edge_init[e].source], + vertices[edge_init[e].target], + g).first; + put(edge_weight, g, edges[e], 1.0); + put(edge_index, g, edges[e], e); + } + + std::vector centrality(V); + std::vector edge_centrality1(E); + + brandes_betweenness_centrality( + g, + centrality_map( + make_iterator_property_map(centrality.begin(), get(vertex_index, g))) + .edge_centrality_map( + make_iterator_property_map(edge_centrality1.begin(), + get(edge_index, g))) + .vertex_index_map(get(vertex_index, g))); + + std::vector centrality2(V); + std::vector edge_centrality2(E); + brandes_betweenness_centrality( + g, + vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g)) + .centrality_map( + make_iterator_property_map(centrality2.begin(), get(vertex_index, g))) + .edge_centrality_map( + make_iterator_property_map(edge_centrality2.begin(), + get(edge_index, g)))); + + std::vector edge_centrality3(E); + brandes_betweenness_centrality( + g, + edge_centrality_map( + make_iterator_property_map(edge_centrality3.begin(), + get(edge_index, g)))); + + for (int v = 0; v < V; ++v) { + BOOST_TEST(centrality[v] == centrality2[v]); + + double relative_error = + correct_centrality[v] == 0.0? centrality[v] + : (centrality[v] - correct_centrality[v]) / correct_centrality[v]; + if (relative_error < 0) relative_error = -relative_error; + BOOST_TEST(relative_error < error_tolerance); + } + + for (int e = 0; e < E; ++e) { + BOOST_TEST(edge_centrality1[e] == edge_centrality2[e]); + BOOST_TEST(edge_centrality1[e] == edge_centrality3[e]); + + if (correct_edge_centrality) { + double relative_error = + correct_edge_centrality[e] == 0.0? edge_centrality1[e] + : (edge_centrality1[e] - correct_edge_centrality[e]) + / correct_edge_centrality[e]; + if (relative_error < 0) relative_error = -relative_error; + BOOST_TEST(relative_error < error_tolerance); + + if (relative_error >= error_tolerance) { + std::cerr << "Edge " << e << " has edge centrality " + << edge_centrality1[e] << ", should be " + << correct_edge_centrality[e] << std::endl; + } + } + } +} + +template +void +run_wheel_test(Graph*, int V) +{ + typedef typename graph_traits::vertex_descriptor Vertex; + typedef typename graph_traits::vertex_iterator vertex_iterator; + + Graph g(V); + Vertex center = *boost::vertices(g).first; + + std::vector vertices(V); + { + vertex_iterator v, v_end; + int index = 0; + for (tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) { + put(vertex_index, g, *v, index); + vertices[index] = *v; + if (*v != center) add_edge(*v, center, g); + } + } + + std::vector centrality(V); + brandes_betweenness_centrality( + g, + make_iterator_property_map(centrality.begin(), get(vertex_index, g))); + + std::vector centrality2(V); + brandes_betweenness_centrality( + g, + centrality_map( + make_iterator_property_map(centrality2.begin(), get(vertex_index, g))) + .vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g))); + + relative_betweenness_centrality( + g, + make_iterator_property_map(centrality.begin(), get(vertex_index, g))); + + relative_betweenness_centrality( + g, + make_iterator_property_map(centrality2.begin(), get(vertex_index, g))); + + for (int v = 0; v < V; ++v) { + BOOST_TEST(centrality[v] == centrality2[v]); + BOOST_TEST((v == 0 && centrality[v] == 1) + || (v != 0 && centrality[v] == 0)); + } + + double dominance = + central_point_dominance( + g, + make_iterator_property_map(centrality2.begin(), get(vertex_index, g))); + BOOST_TEST(dominance == 1.0); +} + +template +void randomly_add_edges(MutableGraph& g, double edge_probability) +{ + typedef typename graph_traits::directed_category + directed_category; + const bool is_undirected = + is_same::value; + + minstd_rand gen; + uniform_01 rand_gen(gen); + + typedef typename graph_traits::vertex_descriptor vertex; + typename graph_traits::vertex_iterator vi, vi_end; + for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { + vertex v = *vi; + typename graph_traits::vertex_iterator wi + = is_undirected? vi : vertices(g).first; + while (wi != vi_end) { + vertex w = *wi++; + if (v != w) { + if (rand_gen() < edge_probability) add_edge(v, w, g); + } + } + } +} + + +template +void +simple_unweighted_betweenness_centrality(const Graph& g, VertexIndexMap index, + CentralityMap centrality) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex; + typedef typename boost::graph_traits::vertex_iterator vertex_iterator; + typedef typename boost::graph_traits::adjacency_iterator adjacency_iterator; + typedef typename boost::graph_traits::vertices_size_type vertices_size_type; + typedef typename boost::property_traits::value_type centrality_type; + + vertex_iterator vi, vi_end; + for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + put(centrality, *vi, 0); + + vertex_iterator si, si_end; + for (tie(si, si_end) = vertices(g); si != si_end; ++si) { + vertex s = *si; + + // S <-- empty stack + std::stack S; + + // P[w] <-- empty list, w \in V + typedef std::vector Predecessors; + std::vector predecessors(num_vertices(g)); + + // sigma[t] <-- 0, t \in V + std::vector sigma(num_vertices(g), 0); + + // sigma[s] <-- 1 + sigma[get(index, s)] = 1; + + // d[t] <-- -1, t \in V + std::vector d(num_vertices(g), -1); + + // d[s] <-- 0 + d[get(index, s)] = 0; + + // Q <-- empty queue + std::queue Q; + + // enqueue s --> Q + Q.push(s); + + while (!Q.empty()) { + // dequeue v <-- Q + vertex v = Q.front(); Q.pop(); + + // push v --> S + S.push(v); + + adjacency_iterator wi, wi_end; + for (tie(wi, wi_end) = adjacent_vertices(v, g); wi != wi_end; ++wi) { + vertex w = *wi; + + // w found for the first time? + if (d[get(index, w)] < 0) { + // enqueue w --> Q + Q.push(w); + + // d[w] <-- d[v] + 1 + d[get(index, w)] = d[get(index, v)] + 1; + } + + // shortest path to w via v? + if (d[get(index, w)] == d[get(index, v)] + 1) { + // sigma[w] = sigma[w] + sigma[v] + sigma[get(index, w)] += sigma[get(index, v)]; + + // append v --> P[w] + predecessors[get(index, w)].push_back(v); + } + } + } + + // delta[v] <-- 0, v \in V + std::vector delta(num_vertices(g), 0); + + // S returns vertices in order of non-increasing distance from s + while (!S.empty()) { + // pop w <-- S + vertex w = S.top(); S.pop(); + + const Predecessors& w_preds = predecessors[get(index, w)]; + for (typename Predecessors::const_iterator vi = w_preds.begin(); + vi != w_preds.end(); ++vi) { + vertex v = *vi; + // delta[v] <-- delta[v] + (sigma[v]/sigma[w])*(1 + delta[w]) + delta[get(index, v)] += + ((centrality_type)sigma[get(index, v)]/sigma[get(index, w)]) + * (1 + delta[get(index, w)]); + } + + if (w != s) { + // C_B[w] <-- C_B[w] + delta[w] + centrality[w] += delta[get(index, w)]; + } + } + } + + typedef typename graph_traits::directed_category directed_category; + const bool is_undirected = + is_same::value; + if (is_undirected) { + vertex_iterator v, v_end; + for(tie(v, v_end) = vertices(g); v != v_end; ++v) { + put(centrality, *v, get(centrality, *v) / centrality_type(2)); + } + } +} + +template +void random_unweighted_test(Graph*, int n) +{ + Graph g(n); + randomly_add_edges(g, 0.20); + std::cout << "Random graph with " << n << " vertices and " + << num_edges(g) << " edges.\n"; + + std::cout << " Direct translation of Brandes' algorithm..."; + std::vector centrality(n); + simple_unweighted_betweenness_centrality(g, get(vertex_index, g), + make_iterator_property_map(centrality.begin(), get(vertex_index, g))); + std::cout << "DONE.\n"; + + std::cout << " Real version, unweighted..."; + std::vector centrality2(n); + brandes_betweenness_centrality(g, + make_iterator_property_map(centrality2.begin(), get(vertex_index, g))); + std::cout << "DONE.\n"; + + BOOST_TEST(std::equal(centrality.begin(), centrality.end(), + centrality2.begin())); + + std::cout << " Real version, weighted..."; + std::vector centrality3(n); + + for (typename graph_traits::edge_iterator ei = edges(g).first; + ei != edges(g).second; ++ei) + put(edge_weight, g, *ei, 1); + + brandes_betweenness_centrality(g, + weight_map(get(edge_weight, g)) + .centrality_map( + make_iterator_property_map(centrality3.begin(), get(vertex_index, g)))); + std::cout << "DONE.\n"; + + BOOST_TEST(std::equal(centrality.begin(), centrality.end(), + centrality3.begin())); + +} + +int test_main(int, char*[]) +{ + typedef adjacency_list, EdgeProperties> + Graph; + typedef adjacency_list, EdgeProperties> + Digraph; + + struct unweighted_edge ud_edge_init1[5] = { + { 0, 1 }, + { 0, 3 }, + { 1, 2 }, + { 3, 2 }, + { 2, 4 } + }; + double ud_centrality1[5] = { 0.5, 1.0, 3.5, 1.0, 0.0 }; + run_unweighted_test((Graph*)0, 5, ud_edge_init1, 5, ud_centrality1); + + // Example borrowed from the JUNG test suite + struct unweighted_edge ud_edge_init2[10] = { + { 0, 1 }, + { 0, 6 }, + { 1, 2 }, + { 1, 3 }, + { 2, 4 }, + { 3, 4 }, + { 4, 5 }, + { 5, 8 }, + { 7, 8 }, + { 6, 7 }, + }; + double ud_centrality2[9] = { + 0.2142 * 28, + 0.2797 * 28, + 0.0892 * 28, + 0.0892 * 28, + 0.2797 * 28, + 0.2142 * 28, + 0.1666 * 28, + 0.1428 * 28, + 0.1666 * 28 + }; + double ud_edge_centrality2[10] = { + 10.66666, + 9.33333, + 6.5, + 6.5, + 6.5, + 6.5, + 10.66666, + 9.33333, + 8.0, + 8.0 + }; + + run_unweighted_test((Graph*)0, 9, ud_edge_init2, 10, ud_centrality2, + ud_edge_centrality2); + + weighted_edge dw_edge_init1[6] = { + { 0, 1, 1.0 }, + { 0, 3, 1.0 }, + { 1, 2, 0.5 }, + { 3, 1, 1.0 }, + { 3, 4, 1.0 }, + { 4, 2, 0.5 } + }; + double dw_centrality1[5] = { 0.0, 1.5, 0.0, 1.0, 0.5 }; + run_weighted_test((Digraph*)0, 5, dw_edge_init1, 6, dw_centrality1); + + run_wheel_test((Graph*)0, 15); + + random_unweighted_test((Graph*)0, 500); + + return 0; +} + diff --git a/test/layout_test.cpp b/test/layout_test.cpp new file mode 100644 index 00000000..3a358298 --- /dev/null +++ b/test/layout_test.cpp @@ -0,0 +1,258 @@ +// Copyright 2004 The Trustees of Indiana University. + +// Use, modification and distribution is subject to 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) + +// Authors: Douglas Gregor +// Andrew Lumsdaine +#include +#include +#include +#include +#include +#include +#include +#include +using namespace boost; + +enum vertex_position_t { vertex_position }; +namespace boost { BOOST_INSTALL_PROPERTY(vertex, position); } + +struct point +{ + double x; + double y; +}; + +template +void print_graph_layout(const Graph& g, PositionMap position) +{ + typename graph_traits::vertex_iterator vi, vi_end; + int xmin = 0, xmax = 0, ymin = 0, ymax = 0; + for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { + if ((int)position[*vi].x < xmin) xmin = (int)position[*vi].x; + if ((int)position[*vi].x > xmax) xmax = (int)position[*vi].x; + if ((int)position[*vi].y < ymin) ymin = (int)position[*vi].y; + if ((int)position[*vi].y > ymax) ymax = (int)position[*vi].y; + } + + for (int y = ymin; y <= ymax; ++y) { + for (int x = xmin; x <= xmax; ++x) { + // Find vertex at this position + typename graph_traits::vertices_size_type index = 0; + for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi, ++index) { + if ((int)position[*vi].x == x && (int)position[*vi].y == y) + break; + } + + if (vi == vi_end) std::cout << ' '; + else std::cout << (char)(index + 'A'); + } + std::cout << std::endl; + } +} + +template +void dump_graph_layout(std::string name, const Graph& g, PositionMap position) +{ + std::ofstream out((name + ".dot").c_str()); + out << "graph " << name << " {" << std::endl; + + typename graph_traits::vertex_iterator vi, vi_end; + for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { + out << " n" << get(vertex_index, g, *vi) << "[ pos=\"" + << (int)position[*vi].x + 25 << ", " << (int)position[*vi].y + 25 + << "\" ];\n"; + } + + typename graph_traits::edge_iterator ei, ei_end; + for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { + out << " n" << get(vertex_index, g, source(*ei, g)) << " -- n" + << get(vertex_index, g, target(*ei, g)) << ";\n"; + } + out << "}\n"; +} + +template +void +test_circle_layout(Graph*, typename graph_traits::vertices_size_type n) +{ + typedef typename graph_traits::vertex_descriptor vertex; + typedef typename graph_traits::vertex_iterator vertex_iterator; + typedef typename graph_traits::vertices_size_type vertices_size_type; + typedef typename graph_traits::edges_size_type edges_size_type; + + Graph g(n); + + // Initialize vertex indices + vertex_iterator vi = vertices(g).first; + for (vertices_size_type i = 0; i < n; ++i, ++vi) + put(vertex_index, g, *vi, i); + + circle_graph_layout(g, get(vertex_position, g), 10.0); + + std::cout << "Regular polygon layout with " << n << " points.\n"; + print_graph_layout(g, get(vertex_position, g)); +} + +struct simple_edge +{ + int first, second; +}; + +struct kamada_kawai_done +{ + kamada_kawai_done() : last_delta() {} + + template + bool operator()(double delta_p, + typename boost::graph_traits::vertex_descriptor p, + const Graph& g, + bool global) + { + if (global) { + double diff = last_delta - delta_p; + if (diff < 0) diff = -diff; + last_delta = delta_p; + return diff < 0.01; + } else { + return delta_p < 0.01; + } + } + + double last_delta; +}; + +template +void +test_triangle(Graph*) +{ + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + typedef typename graph_traits::edge_descriptor edge_descriptor; + + Graph g; + + vertex_descriptor u = add_vertex(g); put(vertex_index, g, u, 0); + vertex_descriptor v = add_vertex(g); put(vertex_index, g, v, 1); + vertex_descriptor w = add_vertex(g); put(vertex_index, g, w, 2); + + edge_descriptor e1 = add_edge(u, v, g).first; put(edge_weight, g, e1, 1.0); + edge_descriptor e2 = add_edge(v, w, g).first; put(edge_weight, g, e2, 1.0); + edge_descriptor e3 = add_edge(w, u, g).first; put(edge_weight, g, e3, 1.0); + + circle_graph_layout(g, get(vertex_position, g), 25.0); + + bool ok = kamada_kawai_spring_layout(g, + get(vertex_position, g), + get(edge_weight, g), + side_length(50.0)); + BOOST_TEST(ok); + + std::cout << "Triangle layout.\n"; + print_graph_layout(g, get(vertex_position, g)); +} + +template +void +test_cube(Graph*) +{ + enum {A, B, C, D, E, F, G, H}; + simple_edge cube_edges[12] = { + {A, E}, {A, B}, {A, D}, {B, F}, {B, C}, {C, D}, {C, G}, {D, H}, + {E, H}, {E, F}, {F, G}, {G, H} + }; + + Graph g(&cube_edges[0], &cube_edges[12], 8); + + typedef typename graph_traits::edge_iterator edge_iterator; + typedef typename graph_traits::vertex_iterator vertex_iterator; + + vertex_iterator vi, vi_end; + int i = 0; + for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + put(vertex_index, g, *vi, i++); + + edge_iterator ei, ei_end; + for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { + put(edge_weight, g, *ei, 1.0); + std::cerr << "(" << (char)(get(vertex_index, g, source(*ei, g)) + 'A') + << ", " << (char)(get(vertex_index, g, target(*ei, g)) + 'A') + << ") "; + } + std::cerr << std::endl; + + circle_graph_layout(g, get(vertex_position, g), 25.0); + + bool ok = kamada_kawai_spring_layout(g, + get(vertex_position, g), + get(edge_weight, g), + side_length(50.0), + kamada_kawai_done()); + BOOST_TEST(ok); + + std::cout << "Cube layout.\n"; + print_graph_layout(g, get(vertex_position, g)); + + dump_graph_layout("cube", g, get(vertex_position, g)); +} + +template +void +test_triangular(Graph*) +{ + enum {A, B, C, D, E, F, G, H, I, J}; + simple_edge triangular_edges[18] = { + {A, B}, {A, C}, {B, C}, {B, D}, {B, E}, {C, E}, {C, F}, {D, E}, {D, G}, + {D, H}, {E, F}, {E, H}, {E, I}, {F, I}, {F, J}, {G, H}, {H, I}, {I, J} + }; + + Graph g(&triangular_edges[0], &triangular_edges[18], 10); + + typedef typename graph_traits::edge_iterator edge_iterator; + typedef typename graph_traits::vertex_iterator vertex_iterator; + + vertex_iterator vi, vi_end; + int i = 0; + for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + put(vertex_index, g, *vi, i++); + + edge_iterator ei, ei_end; + for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { + put(edge_weight, g, *ei, 1.0); + std::cerr << "(" << (char)(get(vertex_index, g, source(*ei, g)) + 'A') + << ", " << (char)(get(vertex_index, g, target(*ei, g)) + 'A') + << ") "; + } + std::cerr << std::endl; + + circle_graph_layout(g, get(vertex_position, g), 25.0); + + bool ok = kamada_kawai_spring_layout(g, + get(vertex_position, g), + get(edge_weight, g), + side_length(50.0), + kamada_kawai_done()); + BOOST_TEST(ok); + + std::cout << "Triangular layout.\n"; + print_graph_layout(g, get(vertex_position, g)); + + dump_graph_layout("triangular", g, get(vertex_position, g)); +} + +int test_main(int, char*[]) +{ + typedef adjacency_list >, + // Edge properties + property > Graph; + + test_circle_layout((Graph*)0, 5); + test_cube((Graph*)0); + test_triangular((Graph*)0); + + return 0; +}