From 0b66e272dc498358778aa6d81c0dd99adbabb136 Mon Sep 17 00:00:00 2001 From: Tiago de Paula Peixoto Date: Fri, 8 Feb 2008 11:22:27 -0200 Subject: [PATCH] Refactor metaprogramming engine This is a huge commit which completely refactors the metaprogramming engine which generates and selects (at run time) the graph view type and the desired algorithm implementation (template instantiation) that runs on it. Things are laid out now as following. There exists a main underlying graph type (GraphInterface::multigraph_t) and several other template classes that mask it some way or another, in a hierarchic fashion: multigraph_t -> filtered_graph (edges only, vertices only, both) | | | | | | | | |-------(reversed_graph)--------|-----------|-----------| | | | | \------(UndirectedAdaptor)------------------------------/ The filtered_graph filters out edges and/or vertices from the graph based on some scalar boolean property. The reversed_graph reversed the direction of the edges and, finally, the UndirectedAdaptor treats the original directed graphs as undirected, transversing the in- and out-edges of each vertex indifferently. Thus, the total number of graph view types is 12. (The option --disable-graph-filtering can be passed to the configure script, which will disable graph filtering altogether and bring the total number down to 3, to reduce compile time and memory usage) In general, some specific algorithm, implemented as a template function object, needs to be instantiated for each of those types. Furthermore, the algorithm may also depend on other types, such as specific property_maps. Thus, the following scheme is used: struct my_algorithm // algorithm to be implemented { template void operator()(Graph *g, PropertyMap p, double& result) const { // ... } }; // in order for the above code to be instantiated at compile time // and selected at run time, the run_action template function object // is used from a member function of the GraphInterface class: double GraphInterface::MyAlgorithm(string prop_name) { double result; boost::any vprop = prop(property, _vertex_index, _properties); run_action<>()(*this, bind(my_algorithm(), _1, _2, var(result)), vertex_scalar_properties())(vprop); return result; } The whole code was changed to reflect this scheme, but now things are more centralized and less ad-hoc code needed to be written. Unfortunately, due to GCC's high memory usage during template instantiations, some of the code (namely all the degree correlation things) had to be split in multiple compilation units... Maybe this will change in the future if GCC gets optimized. This commit also touches other parts of code. More specifically, the way filtering gets done is very different. Now we only filter on boolean properties, and with the above scheme, the desired implementation runs with the correct chosen type, and no implicit type conversions should ever happen, which would have a bad impact on performance. --- configure.in | 35 +- ltmain.sh | 110 +-- src/graph/Makefile.am | 107 ++- src/graph/graph.cc | 456 ++-------- src/graph/graph.hh | 107 +-- src/graph/graph_adaptor.hh | 2 + src/graph/graph_assortativity.cc | 263 +----- src/graph/graph_assortativity.hh | 199 +++++ src/graph/graph_betweenness.cc | 231 +++-- src/graph/graph_bind.cc | 60 +- src/graph/graph_clustering.cc | 194 +--- src/graph/graph_clustering.hh | 174 ++++ src/graph/graph_community.cc | 844 ++---------------- src/graph/graph_community.hh | 744 +++++++++++++++ src/graph/graph_community_network.cc | 209 +---- src/graph/graph_community_network.hh | 220 +++++ src/graph/graph_correlations.cc | 173 +--- src/graph/graph_correlations.hh | 73 ++ src/graph/graph_correlations_combined.cc | 234 +---- src/graph/graph_correlations_combined.hh | 107 +++ src/graph/graph_correlations_combined_corr.cc | 56 ++ src/graph/graph_correlations_imp1.cc | 51 ++ src/graph/graph_correlations_imp2.cc | 49 + src/graph/graph_correlations_imp3.cc | 49 + src/graph/graph_correlations_neighbours.cc | 236 ++--- src/graph/graph_correlations_neighbours.hh | 100 +++ .../graph_correlations_neighbours_imp1.cc | 48 + .../graph_correlations_neighbours_imp2.cc | 48 + .../graph_correlations_neighbours_imp3.cc | 48 + .../graph_correlations_neighbours_imp4.cc | 48 + .../graph_correlations_neighbours_imp5.cc | 48 + .../graph_correlations_neighbours_imp6.cc | 44 + src/graph/graph_distance.cc | 128 +-- src/graph/graph_distance.hh | 116 +++ src/graph/graph_distance_sampled.cc | 139 +-- src/graph/graph_distance_sampled.hh | 129 +++ src/graph/graph_edge_correlations.cc | 126 +-- src/graph/graph_edge_correlations.hh | 75 ++ src/graph/graph_edge_correlations_imp1.cc | 48 + src/graph/graph_edge_correlations_imp2.cc | 48 + src/graph/graph_edge_correlations_imp3.cc | 48 + src/graph/graph_edge_correlations_imp4.cc | 48 + src/graph/graph_edge_correlations_imp5.cc | 48 + src/graph/graph_extended_clustering.cc | 239 ++--- src/graph/graph_extended_clustering.hh | 209 +++++ src/graph/graph_filtering.cc | 339 +++++++ src/graph/graph_filtering.hh | 808 ++++++++++------- src/graph/graph_generation.cc | 2 - src/graph/graph_io.cc | 63 +- src/graph/graph_layout.cc | 308 ++++--- src/graph/graph_line_graph.cc | 21 +- src/graph/graph_minimum_spanning_tree.cc | 94 +- src/graph/graph_properties.cc | 294 ++---- src/graph/graph_properties.hh | 362 ++++++-- src/graph/graph_python_interface.cc | 310 +++---- src/graph/graph_python_interface.hh | 173 +++- src/graph/graph_python_interface_export.cc | 86 ++ src/graph/graph_reciprocity.cc | 12 +- src/graph/graph_rewiring.cc | 564 +----------- src/graph/graph_rewiring.hh | 574 ++++++++++++ src/graph/graph_selectors.cc | 46 + src/graph/graph_selectors.hh | 206 +++-- src/graph/graph_util.hh | 233 +++++ src/graph/mpl_nested_loop.hh | 334 +++++++ 64 files changed, 6686 insertions(+), 4911 deletions(-) create mode 100644 src/graph/graph_assortativity.hh create mode 100644 src/graph/graph_clustering.hh create mode 100644 src/graph/graph_community.hh create mode 100644 src/graph/graph_community_network.hh create mode 100644 src/graph/graph_correlations.hh create mode 100644 src/graph/graph_correlations_combined.hh create mode 100644 src/graph/graph_correlations_combined_corr.cc create mode 100644 src/graph/graph_correlations_imp1.cc create mode 100644 src/graph/graph_correlations_imp2.cc create mode 100644 src/graph/graph_correlations_imp3.cc create mode 100644 src/graph/graph_correlations_neighbours.hh create mode 100644 src/graph/graph_correlations_neighbours_imp1.cc create mode 100644 src/graph/graph_correlations_neighbours_imp2.cc create mode 100644 src/graph/graph_correlations_neighbours_imp3.cc create mode 100644 src/graph/graph_correlations_neighbours_imp4.cc create mode 100644 src/graph/graph_correlations_neighbours_imp5.cc create mode 100644 src/graph/graph_correlations_neighbours_imp6.cc create mode 100644 src/graph/graph_distance.hh create mode 100644 src/graph/graph_distance_sampled.hh create mode 100644 src/graph/graph_edge_correlations.hh create mode 100644 src/graph/graph_edge_correlations_imp1.cc create mode 100644 src/graph/graph_edge_correlations_imp2.cc create mode 100644 src/graph/graph_edge_correlations_imp3.cc create mode 100644 src/graph/graph_edge_correlations_imp4.cc create mode 100644 src/graph/graph_edge_correlations_imp5.cc create mode 100644 src/graph/graph_extended_clustering.hh create mode 100644 src/graph/graph_filtering.cc create mode 100644 src/graph/graph_python_interface_export.cc create mode 100644 src/graph/graph_rewiring.hh create mode 100644 src/graph/graph_selectors.cc create mode 100644 src/graph/graph_util.hh create mode 100644 src/graph/mpl_nested_loop.hh diff --git a/configure.in b/configure.in index 3171a074..7da17ade 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,23 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(graph-tool, 1.2.0devel, [http://graph-tool.forked.de]) +dnl graph-tool package version number +dnl An odd micro number indicates in-progress development. +dnl An even micro number indicates a released version. +m4_define(graph_tool_version_major, 1) +m4_define(graph_tool_version_minor, 9) +m4_define(graph_tool_version_micro, 0) + +AC_INIT([graph-tool], + [graph_tool_version_major().graph_tool_version_minor().graph_tool_version_micro()], + [http://graph-tool.forked.de]) + +GRAPH_TOOL_VERSION_MAJOR=graph_tool_version_major() +GRAPH_TOOL_VERSION_MINOR=graph_tool_version_minor() +GRAPH_TOOL_VERSION_MICRO=graph_tool_version_micro() +AC_SUBST(GRAPH_TOOL_VERSION_MAJOR) +AC_SUBST(GRAPH_TOOL_VERSION_MINOR) +AC_SUBST(GRAPH_TOOL_VERSION_MICRO) + AC_CONFIG_SRCDIR(src/graph-tool) AM_INIT_AUTOMAKE AM_PROG_CC_C_O @@ -74,19 +91,19 @@ AC_ARG_ENABLE([visibility], [AC_HELP_STRING([--disable-visibility], ) -AC_MSG_CHECKING(whether to enable graph range filtering...) -AC_ARG_ENABLE([range-filtering], [AC_HELP_STRING([--disable-range-filtering], - [disable range filtering [default=enabled] ])], +AC_MSG_CHECKING(whether to enable graph filtering...) +AC_ARG_ENABLE([graph-filtering], [AC_HELP_STRING([--disable-graph-filtering], + [disable graph filtering [default=enabled] ])], if test $enableval = no; then [AC_MSG_RESULT(no)] - [AC_DEFINE([NO_RANGE_FILTERING], 1, [disable range filtering])] - NO_RANGE_FILTERING=yes + [AC_DEFINE([NO_GRAPH_FILTERING], 1, [disable graph filtering])] + NO_GRAPH_FILTERING=yes else [AC_MSG_RESULT(yes)] fi , [AC_MSG_RESULT(yes)] - NO_RANGE_FILTERING=no + NO_GRAPH_FILTERING=no ) AC_MSG_CHECKING(whether to enable parallel algorithms with openmp...) @@ -126,8 +143,8 @@ dnl AX_BOOST_PYTHON dnl expat AC_CHECK_LIB(expat,main) -AM_PATH_PYTHON([2.4]) -AC_PYTHON_DEVEL(>= '2.4') +AM_PATH_PYTHON([2.5]) +AC_PYTHON_DEVEL(>= '2.5') dnl Checks for header files. AC_CHECK_HEADER([expat.h]) diff --git a/ltmain.sh b/ltmain.sh index d74b5a67..e589475d 100644 --- a/ltmain.sh +++ b/ltmain.sh @@ -2,7 +2,7 @@ # NOTE: Changing this file will not affect anything until you rerun configure. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, -# 2007 Free Software Foundation, Inc. +# 2007, 2008 Free Software Foundation, Inc. # Originally by Gordon Matzigkeit , 1996 # # This program is free software; you can redistribute it and/or modify @@ -43,8 +43,8 @@ EXIT_FAILURE=1 PROGRAM=ltmain.sh PACKAGE=libtool -VERSION=1.5.24 -TIMESTAMP=" (1.1220.2.456 2007/06/24 02:25:32)" +VERSION=1.5.26 +TIMESTAMP=" (1.1220.2.493 2008/02/01 16:58:18)" # Be Bourne compatible (taken from Autoconf:_AS_BOURNE_COMPATIBLE). if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then @@ -113,15 +113,21 @@ esac # These must not be set unconditionally because not all systems understand # e.g. LANG=C (notably SCO). # We save the old values to restore during execute mode. -for lt_var in LANG LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +lt_env= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${$lt_var+set}\" = set; then save_$lt_var=\$$lt_var + lt_env=\"$lt_var=\$$lt_var \$lt_env\" $lt_var=C export $lt_var fi" done +if test -n "$lt_env"; then + lt_env="env $lt_env" +fi + # Make sure IFS has a sensible default lt_nl=' ' @@ -499,7 +505,7 @@ do echo "\ $PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP -Copyright (C) 2007 Free Software Foundation, Inc. +Copyright (C) 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." exit $? @@ -802,6 +808,7 @@ if test -z "$show_help"; then *.for) xform=for ;; *.java) xform=java ;; *.obj) xform=obj ;; + *.sx) xform=sx ;; esac libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` @@ -970,7 +977,7 @@ EOF $run $rm "$lobj" "$output_obj" $show "$command" - if $run eval "$command"; then : + if $run eval $lt_env "$command"; then : else test -n "$output_obj" && $run $rm $removelist exit $EXIT_FAILURE @@ -1042,7 +1049,7 @@ EOF command="$command$suppress_output" $run $rm "$obj" "$output_obj" $show "$command" - if $run eval "$command"; then : + if $run eval $lt_env "$command"; then : else $run $rm $removelist exit $EXIT_FAILURE @@ -1175,6 +1182,7 @@ EOF thread_safe=no vinfo= vinfo_number=no + single_module="${wl}-single_module" func_infer_tag $base_compile @@ -1660,6 +1668,11 @@ EOF continue ;; + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + -module) module=yes continue @@ -2163,7 +2176,12 @@ EOF continue fi name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` - for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib="$searchdir/lib${name}${search_ext}" @@ -2959,12 +2977,18 @@ EOF # we do not want to link against static libs, # but need to link against shared eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + eval deplibdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names" ; then for tmp in $deplibrary_names ; do depdepl=$tmp done - if test -f "$path/$depdepl" ; then + if test -f "$deplibdir/$depdepl" ; then + depdepl="$deplibdir/$depdepl" + elif test -f "$path/$depdepl" ; then depdepl="$path/$depdepl" + else + # Can't find it, oh well... + depdepl= fi # do not add paths which are already there case " $newlib_search_path " in @@ -3112,9 +3136,10 @@ EOF case $linkmode in oldlib) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 - fi + case " $deplibs" in + *\ -l* | *\ -L*) + $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 ;; + esac if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 @@ -4251,9 +4276,10 @@ EOF ;; obj) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 - fi + case " $deplibs" in + *\ -l* | *\ -L*) + $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 ;; + esac if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 @@ -5688,53 +5714,9 @@ fi\ $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 exit $EXIT_FAILURE fi - if test "X$EGREP" = X ; then - EGREP=egrep - fi - # We do not want portage's install root ($D) present. Check only for - # this if the .la is being installed. - if test "$installed" = yes && test "$D"; then - eval mynewdependency_lib=`echo "$libdir/$name" |sed -e "s:$D:/:g" -e 's:/\+:/:g'` - else - mynewdependency_lib="$libdir/$name" - fi - # Do not add duplicates - if test "$mynewdependency_lib"; then - my_little_ninja_foo_1=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"` - if test -z "$my_little_ninja_foo_1"; then - newdependency_libs="$newdependency_libs $mynewdependency_lib" - fi - fi - ;; - *) - if test "$installed" = yes; then - # Rather use S=WORKDIR if our version of portage supports it. - # This is because some ebuild (gcc) do not use $S as buildroot. - if test "$PWORKDIR"; then - S="$PWORKDIR" - fi - # We do not want portage's build root ($S) present. - my_little_ninja_foo_2=`echo $deplib |$EGREP -e "$S"` - # We do not want portage's install root ($D) present. - my_little_ninja_foo_3=`echo $deplib |$EGREP -e "$D"` - if test -n "$my_little_ninja_foo_2" && test "$S"; then - mynewdependency_lib="" - elif test -n "$my_little_ninja_foo_3" && test "$D"; then - eval mynewdependency_lib=`echo "$deplib" |sed -e "s:$D:/:g" -e 's:/\+:/:g'` - else - mynewdependency_lib="$deplib" - fi - else - mynewdependency_lib="$deplib" - fi - # Do not add duplicates - if test "$mynewdependency_lib"; then - my_little_ninja_foo_4=`echo $newdependency_libs |$EGREP -e "$mynewdependency_lib"` - if test -z "$my_little_ninja_foo_4"; then - newdependency_libs="$newdependency_libs $mynewdependency_lib" - fi - fi + newdependency_libs="$newdependency_libs $libdir/$name" ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; esac done dependency_libs="$newdependency_libs" @@ -5786,10 +5768,6 @@ fi\ case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; esac - # Do not add duplicates - if test "$installed" = yes && test "$D"; then - install_libdir=`echo "$install_libdir" |sed -e "s:$D:/:g" -e 's:/\+:/:g'` - fi $echo > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP @@ -6545,7 +6523,7 @@ relink_command=\"$relink_command\"" fi # Restore saved environment variables - for lt_var in LANG LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var diff --git a/src/graph/Makefile.am b/src/graph/Makefile.am index d3165d1f..3cd64079 100644 --- a/src/graph/Makefile.am +++ b/src/graph/Makefile.am @@ -19,30 +19,47 @@ libgraph_tool_LTLIBRARIES = libgraph_tool.la libgraph_tool_la_includedir = $(pythondir)/graph_tool/include libgraph_tool_la_SOURCES = \ - graph.hh\ - graph.cc\ - graph_python_interface.cc\ - graph_properties.cc\ - graph_correlations.cc\ - graph_edge_correlations.cc\ - graph_correlations_combined.cc\ - graph_correlations_neighbours.cc\ - graph_assortativity.cc\ - graph_clustering.cc\ - graph_extended_clustering.cc\ - graph_generation.cc\ - graph_distance.cc\ - graph_distance_sampled.cc\ - graph_reciprocity.cc\ - graph_minimum_spanning_tree.cc\ - graph_community.cc\ - graph_community_network.cc\ - graph_line_graph.cc\ - graph_betweenness.cc\ - graph_rewiring.cc\ - graph_layout.cc\ - graph_io.cc\ - graph_bind.cc\ + graph_assortativity.cc \ + graph_betweenness.cc \ + graph_bind.cc \ + graph.cc \ + graph_clustering.cc \ + graph_community.cc \ + graph_community_network.cc \ + graph_correlations.cc \ + graph_correlations_combined.cc \ + graph_correlations_combined_corr.cc \ + graph_correlations_imp1.cc \ + graph_correlations_imp2.cc \ + graph_correlations_imp3.cc \ + graph_correlations_neighbours.cc \ + graph_correlations_neighbours_imp1.cc \ + graph_correlations_neighbours_imp2.cc \ + graph_correlations_neighbours_imp3.cc \ + graph_correlations_neighbours_imp4.cc \ + graph_correlations_neighbours_imp5.cc \ + graph_correlations_neighbours_imp6.cc \ + graph_distance.cc \ + graph_distance_sampled.cc \ + graph_edge_correlations.cc \ + graph_edge_correlations_imp1.cc \ + graph_edge_correlations_imp2.cc \ + graph_edge_correlations_imp3.cc \ + graph_edge_correlations_imp4.cc \ + graph_edge_correlations_imp5.cc \ + graph_extended_clustering.cc \ + graph_filtering.cc \ + graph_generation.cc \ + graph_io.cc \ + graph_layout.cc \ + graph_line_graph.cc \ + graph_minimum_spanning_tree.cc \ + graph_properties.cc \ + graph_python_interface.cc \ + graph_python_interface_export.cc \ + graph_reciprocity.cc \ + graph_rewiring.cc \ + graph_selectors.cc \ graphml.cpp\ read_graphviz_spirit.cpp\ ../boost-workaround/boost/graph/filtered_graph.hpp\ @@ -50,16 +67,42 @@ libgraph_tool_la_SOURCES = \ ../boost-workaround/boost/graph/graphml.hpp libgraph_tool_la_include_HEADERS = \ - graph_adaptor.hh\ - graph.hh\ - graph_filtering.hh\ - graph_python_interface.hh\ - graph_selectors.hh\ - graph_properties.hh\ - shared_map.hh\ - histogram.hh\ + graph_adaptor.hh \ + graph_assortativity.hh \ + graph_clustering.hh \ + graph_community.hh \ + graph_community_network.hh \ + graph_correlations_combined.hh \ + graph_correlations.hh \ + graph_correlations_neighbours.hh \ + graph_distance.hh \ + graph_distance_sampled.hh \ + graph_edge_correlations.hh \ + graph_extended_clustering.hh \ + graph_filtering.hh \ + graph.hh \ + graph_properties.hh \ + graph_python_interface.hh \ + graph_rewiring.hh \ + graph_selectors.hh \ + graph_util.hh \ + histogram.hh \ + mpl_nested_loop.hh \ + shared_map.hh \ ../../config.h +BUILT_SOURCES = \ + graph_filtering.hh.gch + +## Header precompilation +## FIXME: need a better way to convince libtool to let us do this. +$(libgraph_tool_la_include_HEADERS): + +$(BUILT_SOURCES): + $(CXXCOMPILE) $(CXXFLAGS) $(AM_CXXFLAGS) $(AM_CPPFLAGS) -fPIC -DPIC -x c++-header `basename $@ .gch` +mostlyclean-local: + -rm -f *.gch + libgraph_tool_la_LIBADD = \ $(PYTHON_LDFLAGS) \ $(BOOST_LDFLAGS) \ diff --git a/src/graph/graph.cc b/src/graph/graph.cc index 0e8aa96e..df131e42 100644 --- a/src/graph/graph.cc +++ b/src/graph/graph.cc @@ -35,6 +35,7 @@ #include "graph_filtering.hh" #include "graph_selectors.hh" #include "graph_properties.hh" +#include "graph_util.hh" #include "shared_map.hh" using namespace std; @@ -42,24 +43,6 @@ using namespace boost; using namespace boost::lambda; using namespace graph_tool; -// this will return the degree type contained in 'degree' or the name of the -// scalar property. -pair -graph_tool::get_degree_type(GraphInterface::deg_t degree) -{ - GraphInterface::degree_t deg; - string name; - try - { - deg = boost::get(degree); - } - catch (bad_get) - { - name = boost::get(degree); - deg = GraphInterface::SCALAR; - } - return make_pair(deg,name); -} // this is the constructor for the graph interface GraphInterface::GraphInterface() @@ -69,13 +52,11 @@ GraphInterface::GraphInterface() _vertex_index(get(vertex_index,_mg)), _edge_index(get(edge_index_t(),_mg)), _vertex_filter_map(_vertex_index), - _vertex_range(make_pair(0.0, numeric_limits::max())), - _vertex_range_include(make_pair(true, true)), - _vertex_range_invert(false), + _vertex_filter_invert(false), + _vertex_filter_active(false), _edge_filter_map(_edge_index), - _edge_range(make_pair(0.0, numeric_limits::max())), - _edge_range_include(make_pair(true, true)), - _edge_range_invert(false) + _edge_filter_invert(false), + _edge_filter_active(false) { } @@ -85,313 +66,15 @@ GraphInterface::~GraphInterface() { } -// these test whether or not the vertex and filter are active, by checking the -// corresponding property value -bool GraphInterface::IsVertexFilterActive() const -{ return _vertex_filter_property != ""; } - -bool GraphInterface::IsEdgeFilterActive() const -{ return _edge_filter_property != ""; } - -// this function will get a dynamic property map, and return its static type, if -// it matches a couple of possibilities that have 'double' as value -// type. Otherwise it wraps it around a DynamicPropertyMapWrap -template -FilterMap choose_filter_map(string property, dynamic_properties& dp) -{ - FilterMap filter_map; - try - { - dynamic_property_map& pmap = - find_property_map(dp, property, typeid(Descriptor)); - - try - { - return get_static_property_map - >(pmap); - } - catch(bad_cast){} - - try - { - return get_static_property_map - >(pmap); - } - catch(bad_cast){} - - try - { - return get_static_property_map - >(pmap); - } - catch(bad_cast){} - - try - { - return get_static_property_map - >(pmap); - } - catch(bad_cast){} - - try - { - return get_static_property_map(pmap); - } - catch(bad_cast){} - - return DynamicPropertyMapWrap(pmap); - } - catch (property_not_found) - { - throw GraphException("property " + property + " not found"); - } - -} - -// this will set enable the vertex filter, according to the given property. If -// property is an empty string, this will disable the filter -void GraphInterface::SetVertexFilterProperty(string property) -{ -#ifndef NO_RANGE_FILTERING - _vertex_filter_property = property; - - if (property != "") - { - _vertex_filter_map = choose_filter_map - (property, _properties); - } - else - { - _vertex_filter_map = _vertex_index; - _vertex_range = make_pair(0.0, numeric_limits::max()); - _vertex_range_include = make_pair(true, true); - _vertex_range_invert = false; - } -#else - if (property != "") - throw GraphException("support for graph range filtering was not enabled" - " during compilation."); -#endif -} - -// this will set enable the edge filter, according to the given property. If -// property is an empty string, this will disable the filter -void GraphInterface::SetEdgeFilterProperty(string property) -{ -#ifndef NO_RANGE_FILTERING - _edge_filter_property = property; - - if (property != "") - { - _edge_filter_map = choose_filter_map - (property, _properties); - } - else - { - _edge_filter_map = _edge_index; - _edge_range = make_pair(0.0, numeric_limits::max()); - _edge_range_include = make_pair(true, true); - _edge_range_invert = false; - } -#else - if (property != "") - throw GraphException("support for graph range filtering was not enabled" - " during compilation."); -#endif -} - - -void GraphInterface::SetVertexFilterRange(pair allowed_range, - pair include, bool invert) -{ -#ifndef NO_RANGE_FILTERING - if (isinf(allowed_range.first) == 1) - allowed_range.first = numeric_limits::max(); - else if (isinf(allowed_range.first) == -1) - allowed_range.first = -numeric_limits::max(); - if (isinf(allowed_range.second) == 1) - allowed_range.second = numeric_limits::max(); - else if (isinf(allowed_range.second) == -1) - allowed_range.second = -numeric_limits::max(); - - _vertex_range = allowed_range; - _vertex_range_include = include; - _vertex_range_invert = invert; -#else - throw GraphException("support for graph range filtering was not enabled" - " during compilation."); -#endif -} - -void GraphInterface::SetEdgeFilterRange(pair allowed_range, - pair include, bool invert) -{ -#ifndef NO_RANGE_FILTERING - if (isinf(allowed_range.first) == 1) - allowed_range.first = numeric_limits::max(); - else if (isinf(allowed_range.first) == -1) - allowed_range.first = -numeric_limits::max(); - if (isinf(allowed_range.second) == 1) - allowed_range.second = numeric_limits::max(); - else if (isinf(allowed_range.second) == -1) - allowed_range.second = -numeric_limits::max(); - - _edge_range = allowed_range; - _edge_range_include = include; - _edge_range_invert = invert; -#else - throw GraphException("support for graph range filtering was not enabled" - " during compilation."); -#endif -} - - -// this function will reindex all the edges, in the order in which they are -// found, taking care of preserving all the properties -void GraphInterface::ReIndexEdges() -{ - size_t n_edges = num_edges(_mg); - if (n_edges == 0) - return; - vector > edge_map - (n_edges, make_pair(edge_t(), false)); - graph_traits::vertex_iterator v, v_end; - graph_traits::out_edge_iterator e, e_end; - for (tie(v, v_end) = vertices(_mg); v != v_end; ++v) - for (tie(e, e_end) = out_edges(*v, _mg); e != e_end; ++e) - { - size_t index = _edge_index[*e]; - if (index >= edge_map.size()) - edge_map.resize(index+1); - edge_map[index] = make_pair(*e, true); - } - size_t new_index = 0; - for (tie(v, v_end) = vertices(_mg); v != v_end; ++v) - for (tie(e, e_end) = out_edges(*v, _mg); e != e_end; ++e) - { - edge_t old_edge = edge_map[new_index].first; - if (edge_map[new_index].second) - { - // another edge exists with the same index; indexes - // must be swapped, as well as the properties - _edge_index[old_edge] = _edge_index[*e]; - edge_map[_edge_index[*e]] = make_pair(old_edge, true); - _edge_index[*e] = new_index; - edge_map[new_index] = make_pair(*e, true); - for (typeof(_properties.begin()) p = _properties.begin(); - p != _properties.end(); ++p) - if (p->second->key() == typeid(edge_t)) - { - boost::any temp = p->second->get(*e); - p->second->put(*e, p->second->get(old_edge)); - p->second->put(old_edge, temp); - } - } - else - { - // no other edge has this index; it must be then - // assigned for this edge, and the properties must be - // copied over - size_t old_index = _edge_index[*e]; - for (typeof(_properties.begin()) p = _properties.begin(); - p != _properties.end(); ++p) - if (p->second->key() == typeid(edge_t)) - { - _edge_index[*e] = old_index; - boost::any val = p->second->get(*e); - _edge_index[*e] = new_index; - p->second->put(*e, val); - } - } - ++new_index; - } -} - -// this will definitively remove all the edges from the graph, which are being -// currently filtered out. This will also disable the edge filter -void GraphInterface::PurgeEdges() -{ - if (!IsEdgeFilterActive()) - return; - - RangeFilter filter(_edge_filter_map, _edge_range, - _edge_range_include, - _edge_range_invert); - graph_traits::vertex_iterator v, v_end; - graph_traits::out_edge_iterator e, e_end; - vector::edge_descriptor> deleted_edges; - for (tie(v, v_end) = vertices(_mg); v != v_end; ++v) - { - for (tie(e, e_end) = out_edges(*v, _mg); e != e_end; ++e) - if (!filter(*e)) - deleted_edges.push_back(*e); - for (typeof(deleted_edges.begin()) iter = deleted_edges.begin(); - iter != deleted_edges.end(); ++iter) - remove_edge(*iter, _mg); - deleted_edges.clear(); - } - ReIndexEdges(); -} - - -// this will definitively remove all the verticess from the graph, which are -// being currently filtered out. This will also disable the vertex filter -void GraphInterface::PurgeVertices() -{ - if (!IsVertexFilterActive()) - return; - - RangeFilter filter(_vertex_filter_map, _vertex_range, - _vertex_range_include, - _vertex_range_invert); - size_t N = num_vertices(_mg); - vector deleted(N, false); - for (size_t i = 0; i < N; ++i) - deleted[i] = !filter(vertex(i, _mg)); - - //migrate properties - size_t last = 0; - for (size_t i = 0; i < N; ++i) - { - graph_traits::vertex_descriptor v = vertex(i, _mg); - if (filter(v)) - { - for (typeof(_properties.begin()) p = _properties.begin(); - p != _properties.end(); ++p) - if (p->second->key() == typeid(vertex_t)) - try - { - p->second->put(vertex(last, _mg), p->second->get(v)); - } - catch (dynamic_const_put_error) {} // index prop. is const - last++; - } - } - - //remove vertices - for (size_t i = N-1; i < N; --i) - { - if (deleted[i]) - { - graph_traits::vertex_descriptor v = vertex(i, _mg); - clear_vertex(v, _mg); - remove_vertex(v, _mg); - } - } - ReIndexEdges(); -} - // this will get the number of vertices, either the "soft" O(1) way, or the hard // O(V) way, which is necessary if the graph is filtered size_t GraphInterface::GetNumberOfVertices() const { size_t n = 0; if (IsVertexFilterActive()) - run_action(*this,var(n)=bind(HardNumVertices(),_1)); + run_action<>()(*this, var(n)=bind(HardNumVertices(),_1))(); else - run_action(*this,var(n)=bind(SoftNumVertices(),_1)); + run_action<>()(*this, var(n)=bind(SoftNumVertices(),_1))(); return n; } @@ -402,21 +85,19 @@ size_t GraphInterface::GetNumberOfEdges() const { size_t n = 0; if (IsEdgeFilterActive() || IsVertexFilterActive()) - run_action(*this,var(n)=bind(HardNumEdges(),_1)); + run_action<>()(*this, var(n)=bind(HardNumEdges(),_1))(); else - run_action(*this,var(n)=bind(SoftNumEdges(),_1)); + run_action<>()(*this, var(n)=bind(SoftNumEdges(),_1))(); return n; } - // generalized functor to obtain histogram of different types of "degrees" -template struct get_vertex_histogram { - get_vertex_histogram(DegreeSelector& deg): _degree(deg) {} - template - void operator()(const Graph& g, Hist& hist) const + template + void operator()(const Graph* gp, DegreeSelector deg, Hist& hist) const { + const Graph& g = *gp; SharedMap s_hist(hist); int i, N = num_vertices(g); #pragma omp parallel for default(shared) private(i) \ @@ -426,36 +107,10 @@ struct get_vertex_histogram typename graph_traits::vertex_descriptor v = vertex(i, g); if (v == graph_traits::null_vertex()) continue; - s_hist[_degree(v,g)]++; + s_hist[deg(v,g)]++; } s_hist.Gather(); } - DegreeSelector& _degree; -}; - -// used to cycle through the different types of histograms -struct choose_vertex_histogram -{ - choose_vertex_histogram(const GraphInterface& g, GraphInterface::deg_t deg, - hist_t& hist) - : _g(g), _hist(hist) - { - tie(_deg, _deg_name) = get_degree_type(deg); - } - template - void operator()(DegreeSelector) - { - if (mpl::at::type::value == _deg) - { - DegreeSelector selector(_deg_name, _g, true); - run_action(_g, bind(get_vertex_histogram - (selector), _1, var(_hist))); - } - } - const GraphInterface &_g; - hist_t &_hist; - GraphInterface::degree_t _deg; - string _deg_name; }; @@ -464,13 +119,12 @@ hist_t GraphInterface::GetVertexHistogram(GraphInterface::deg_t deg) const { hist_t hist; + try { - mpl::for_each > - (choose_vertex_histogram(*this, deg, hist)); + run_action<>()(*this, bind(get_vertex_histogram(), _1, _2, + var(hist)), + all_selectors())(degree_selector(deg, _properties)); } catch (dynamic_get_failure &e) { @@ -484,12 +138,10 @@ GraphInterface::GetVertexHistogram(GraphInterface::deg_t deg) const // generalized functor to obtain histogram of edge properties struct get_edge_histogram { - get_edge_histogram(scalarS& prop): _prop(prop) {} - template - void operator()(const Graph& g, Hist& hist) const - { - typedef typename graph_traits::directed_category - directed_category; + template + void operator()(const Graph* gp, Prop eprop, Hist& hist) const + { + const Graph& g = *gp; SharedMap s_hist(hist); int i, N = num_vertices(g); @@ -504,28 +156,34 @@ struct get_edge_histogram typename graph_traits::out_edge_iterator e, e_begin, e_end; tie(e_begin,e_end) = out_edges(v,g); for(e = e_begin; e != e_end; ++e) - if(is_convertible::value) - s_hist[_prop(*e,g)] += 0.5; - else - s_hist[_prop(*e,g)]++; + s_hist[eprop[*e]]++; } s_hist.Gather(); + + typedef typename graph_traits::directed_category + directed_category; + if(is_convertible::value) + { + for (typeof(s_hist.begin()) iter = s_hist.begin(); + iter != s_hist.end(); ++iter) + iter->second /= typename Hist::value_type::second_type(2); + } } - scalarS& _prop; }; - // returns the histogram of a given edge property hist_t GraphInterface::GetEdgeHistogram(string property) const { hist_t hist; try { - scalarS prop(property, *this, false); - run_action(*this, bind(get_edge_histogram(prop), _1, var(hist))); + run_action<>()(*this, bind(get_edge_histogram(), _1, _2, + var(hist)), + edge_scalar_properties()) + (prop(property, _edge_index, _properties)); } - catch (dynamic_get_failure &e) + catch (dynamic_get_failure& e) { throw GraphException("error getting scalar property: " + string(e.what())); @@ -540,8 +198,9 @@ hist_t GraphInterface::GetEdgeHistogram(string property) const struct label_components { template - void operator()(const Graph &g, CompMap comp_map) const + void operator()(const Graph* gp, CompMap comp_map) const { + const Graph& g = *gp; typedef typename graph_traits::directed_category directed_category; get_components(g, comp_map, @@ -567,28 +226,28 @@ struct label_components void GraphInterface::LabelComponents(string prop) { - typedef vector_property_map comp_map_t; - comp_map_t comp_map(_vertex_index); - - run_action(*this, bind(label_components(), _1, comp_map)); - try { - find_property_map(_properties, prop, typeid(vertex_t)); - RemoveVertexProperty(prop); + run_action<>()(*this, label_components(), _1, vertex_scalar_selectors()) + (degree_selector(prop, _properties)); + } + catch (property_not_found) + { + typedef vector_property_map comp_map_t; + comp_map_t comp_map(_vertex_index); + _properties.property(prop, comp_map); + run_action<>()(*this, bind(label_components(), _1, comp_map))(); } - catch (property_not_found) {} - - _properties.property(prop, comp_map); } // label parallel edges in the order they are found, starting from 0 struct label_parallel_edges { template - void operator()(const Graph& g, EdgeIndexMap edge_index, + void operator()(const Graph* gp, EdgeIndexMap edge_index, ParallelMap parallel) const { + const Graph& g = *gp; typedef typename graph_traits::edge_descriptor edge_t; int i, N = num_vertices(g); @@ -623,20 +282,19 @@ struct label_parallel_edges void GraphInterface::LabelParallelEdges(string property) { try - { - DynamicPropertyMapWrap - parallel_map(find_property_map(_properties, property, - typeid(edge_t))); - run_action(*this, bind(label_parallel_edges(), _1, _edge_index, - parallel_map)); + { + run_action<>()(*this, bind(label_parallel_edges(), _1, + _edge_index, _2), + edge_scalar_properties()) + (prop(property, _edge_index, _properties)); } catch (property_not_found) { - typedef vector_property_map parallel_map_t; + typedef vector_property_map parallel_map_t; parallel_map_t parallel_map(_edge_index); - run_action(*this, bind(label_parallel_edges(), _1, _edge_index, - parallel_map)); _properties.property(property, parallel_map); + run_action<>()(*this, bind(label_parallel_edges(), _1, + _edge_index, parallel_map))(); } } diff --git a/src/graph/graph.hh b/src/graph/graph.hh index 804f301b..5966f548 100644 --- a/src/graph/graph.hh +++ b/src/graph/graph.hh @@ -21,12 +21,16 @@ #include #include + +#include + #include #include #include #include #include #include +#include #include "histogram.hh" #include "config.h" #include "graph_properties.hh" @@ -41,6 +45,15 @@ using namespace boost; // the external world will manipulate the graph. All the algorithms should be // registered here. This class will be exported to python in graph_bind.hh +namespace detail +{ +// Generic graph_action functor. See graph_filtering.hh for details. +template , class TR2=boost::mpl::vector<>, + class TR3=boost::mpl::vector<>, class TR4=boost::mpl::vector<> > +struct graph_action; +} + // default visibility is necessary for related typeinfo objects to work across // DSO boundaries #pragma GCC visibility push(default) @@ -56,7 +69,6 @@ public: IN_DEGREE, OUT_DEGREE, TOTAL_DEGREE, // in + out - SCALAR // scalar vertex property }; typedef variant deg_t; // useful when function also expects @@ -73,16 +85,10 @@ public: void SetReversed(bool reversed) {_reversed = reversed;} bool GetReversed() const {return _reversed;} - // graph filtering - void SetVertexFilterProperty(string property); - void SetVertexFilterRange(pair allowed_range, - pair include, bool invert); + void SetVertexFilterProperty(string property, bool invert); bool IsVertexFilterActive() const; - - void SetEdgeFilterProperty(string property); - void SetEdgeFilterRange(pair allowed_range, - pair include, bool invert); + void SetEdgeFilterProperty(string property, bool invert); bool IsEdgeFilterActive() const; @@ -92,15 +98,13 @@ public: void RemoveGraphProperty(string property); void InsertEdgeIndexProperty(string property); void InsertVertexIndexProperty(string property); - void EditVertexProperty(string property, string type, python::object op, - python::object g); - void EditEdgeProperty(string property, string type, python::object op, - python::object g); - void EditGraphProperty(string property, string type, python::object op, - python::object g); + void AddVertexProperty(string property, string type); + void AddEdgeProperty(string property, string type); + void AddGraphProperty(string property, string type); void ReIndexEdges(); void PurgeVertices(); // removes filtered vertices void PurgeEdges(); // removes filtered edges + void Clear(); void RandomRewire(std::string strat, bool self_loops, bool parallel_edges, size_t seed); @@ -183,10 +187,13 @@ public: void ComputeGraphLayoutGursoy(string prop, string weight, string topology, size_t iter = 0, size_t seed = 4357); void ComputeGraphLayoutSpringBlock(string prop, string weight, string type, - size_t iter = 0, size_t seed = 4357); + size_t iter = 0, + bool progressive = false, + size_t seed = 4357); // python interface python::object Vertices() const; + python::object Vertex(size_t i) const; python::object Edges() const; python::object AddVertex(); @@ -231,75 +238,51 @@ public: typedef graph_traits::vertex_descriptor vertex_t; typedef graph_traits::edge_descriptor edge_t; -private: - - // The following function is very central to the implementation of the above - // member functions. Most of the algorithms are implemented as template - // functors, which must be run on the correct version of the graph, i.e., - // filtered, unfiltered, directed, undirected, etc. The functor in question - // must be fed to the function below as the "a" variable, which will take - // care of business, selection the correct implementation. The ReverseCheck - // and DirectedCheck below are utility types which allow for limiting the - // implementation generation when you want it to be confined for only - // specific graph types. See graph_filtering.hh for details. + typedef property_map::type vertex_index_map_t; + typedef property_map::type edge_index_map_t; - template - friend void run_action(GraphInterfaceType &g, Action a, - ReverseCheck, DirectedCheck, bool run_all=false); + size_t GetEdgeHash(const edge_t& e) const; - // useful overload for common case where all graph types should be probed - template - friend void run_action(GraphInterfaceType &g, Action a); +private: + // Gets the encapsulated graph view. See graph_filtering.cc for details + boost::any GetGraphView() const; - friend class scalarS; + // Generic graph_action functor. See graph_filtering.hh for details. + template , class TR2=boost::mpl::vector<>, + class TR3=boost::mpl::vector<>, class TR4=boost::mpl::vector<> > + friend struct detail::graph_action; // this is the main graph multigraph_t _mg; + // this will hold an instance of the graph views at run time + vector _graph_views; + // reverse and directed states bool _reversed; bool _directed; // vertex index map - typedef property_map::type vertex_index_map_t; vertex_index_map_t _vertex_index; // edge index map - typedef property_map::type edge_index_map_t; edge_index_map_t _edge_index; // graph properties dynamic_properties _properties; // vertex filter - string _vertex_filter_property; - typedef variant, - HashedDescriptorMap, - vector_property_map, - HashedDescriptorMap, - vertex_index_map_t, - DynamicPropertyMapWrap > - vertex_filter_map_t; - vertex_filter_map_t _vertex_filter_map; - pair _vertex_range; - pair _vertex_range_include; - bool _vertex_range_invert; + typedef vector_property_map vertex_filter_t; + vertex_filter_t _vertex_filter_map; + bool _vertex_filter_invert; + bool _vertex_filter_active; // edge filter - string _edge_filter_property; - typedef variant, - HashedDescriptorMap, - vector_property_map, - HashedDescriptorMap, - edge_index_map_t, - DynamicPropertyMapWrap > - edge_filter_map_t; - edge_filter_map_t _edge_filter_map; - pair _edge_range; - pair _edge_range_include; - bool _edge_range_invert; - + typedef vector_property_map edge_filter_t; + edge_filter_t _edge_filter_map; + bool _edge_filter_invert; + bool _edge_filter_active; }; #pragma GCC visibility pop diff --git a/src/graph/graph_adaptor.hh b/src/graph/graph_adaptor.hh index f1d72e01..c8032cc3 100644 --- a/src/graph/graph_adaptor.hh +++ b/src/graph/graph_adaptor.hh @@ -18,6 +18,8 @@ #ifndef GRAPH_ADAPTOR_HH #define GRAPH_ADAPTOR_HH +#include + #include #include #include diff --git a/src/graph/graph_assortativity.cc b/src/graph/graph_assortativity.cc index ab86c907..c8e5911a 100644 --- a/src/graph/graph_assortativity.cc +++ b/src/graph/graph_assortativity.cc @@ -16,135 +16,18 @@ // along with this program. If not, see . -#include -#include -#include -#include -#include - -#include "graph.hh" -#include "histogram.hh" #include "graph_filtering.hh" #include "graph_selectors.hh" #include "graph_properties.hh" -#include "shared_map.hh" +#include "graph.hh" + +#include "graph_assortativity.hh" using namespace std; using namespace boost; using namespace boost::lambda; using namespace graph_tool; -// this will calculate the assortativity coefficient, based on the property -// pointed by 'deg' - -template -struct get_assortativity_coefficient -{ - get_assortativity_coefficient(DegreeSelector& deg): _deg(deg) {} - - template - void operator()(const Graph& g, double& r, double& r_err) const - { - size_t n_edges = 0; - int e_kk = 0; - tr1::unordered_map a, b; - SharedMap > sa(a), sb(b); - - int i, N = num_vertices(g); - #pragma omp parallel for default(shared) private(i) firstprivate(sa,sb)\ - schedule(dynamic) reduction(+:e_kk, n_edges) - for (i = 0; i < N; ++i) - { - typename graph_traits::vertex_descriptor v = vertex(i, g); - if (v == graph_traits::null_vertex()) - continue; - - double k1 = _deg(v, g); - typename graph_traits::out_edge_iterator e, e_end; - for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) - { - double k2 = _deg(target(*e, g), g); - sa[k1]++; - sb[k2]++; - if (k1 == k2) - e_kk++; - n_edges++; - } - } - - sa.Gather(); - sb.Gather(); - - double t1=double(e_kk)/n_edges, t2=0.0; - - for (typeof(a.begin()) iter = a.begin(); iter != a.end(); ++iter) - if (b.find(iter->second) != b.end()) - t2 += double(iter->second * b[iter->first]); - t2 /= n_edges*n_edges; - - r = (t1 - t2)/(1.0 - t2); - - // "jackknife" variance - double err = 0.0; - #pragma omp parallel for default(shared) private(i) schedule(dynamic)\ - reduction(+:err) - for (i = 0; i < N; ++i) - { - typename graph_traits::vertex_descriptor v = vertex(i, g); - if (v == graph_traits::null_vertex()) - continue; - - double k1 = _deg(v, g); - typename graph_traits::out_edge_iterator e, e_end; - for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) - { - double k2 = _deg(target(*e,g),g); - double tl2 = (t2*(n_edges*n_edges) - b[k1] - a[k2])/ - ((n_edges-1)*(n_edges-1)); - double tl1 = t1*n_edges; - if (k1 == k2) - tl1 -= 1; - tl1 /= n_edges - 1; - double rl = (tl1 - tl2)/(1.0 - tl2); - err += (r-rl)*(r-rl); - } - } - r_err = sqrt(err); - } - - DegreeSelector& _deg; -}; - -struct choose_assortativity_coefficient -{ - choose_assortativity_coefficient(const GraphInterface& g, - GraphInterface::deg_t deg, - double& a, double& a_err) - : _g(g), _a(a), _a_err(a_err) - { - tie(_deg, _deg_name) = get_degree_type(deg); - } - - - template - void operator()(DegreeSelector) - { - if (mpl::at::type::value == _deg) - { - DegreeSelector deg(_deg_name, _g, true); - run_action(_g, bind(get_assortativity_coefficient - (deg), - _1, var(_a), var(_a_err))); - } - } - - const GraphInterface& _g; - double& _a; - double& _a_err; - GraphInterface::degree_t _deg; - string _deg_name; -}; - pair GraphInterface::GetAssortativityCoefficient(GraphInterface::deg_t deg) const @@ -152,10 +35,10 @@ GraphInterface::GetAssortativityCoefficient(GraphInterface::deg_t deg) const double a, a_err; try { - typedef mpl::vector - degrees; - mpl::for_each(choose_assortativity_coefficient(*this, deg, a, - a_err)); + run_action<>()(*this, + bind(get_assortativity_coefficient(), _1, _2, + var(a), var(a_err)), all_selectors()) + (degree_selector(deg, _properties)); } catch (dynamic_get_failure &e) { @@ -166,130 +49,6 @@ GraphInterface::GetAssortativityCoefficient(GraphInterface::deg_t deg) const return make_pair(a, a_err); } -// this will calculate the _scalar_ assortativity coefficient, based on the -// scalar property pointed by 'deg' - -template -struct get_scalar_assortativity_coefficient -{ - get_scalar_assortativity_coefficient(DegreeSelector& deg): _deg(deg) {} - - template - void operator()(const Graph& g, double& r, double& r_err) const - { - size_t n_edges = 0; - double e_xy = 0.0; - tr1::unordered_map a, b; - SharedMap > sa(a), sb(b); - - int i, N = num_vertices(g); - #pragma omp parallel for default(shared) private(i) firstprivate(sa,sb)\ - schedule(dynamic) reduction(+:e_xy, n_edges) - for (i = 0; i < N; ++i) - { - typename graph_traits::vertex_descriptor v = vertex(i, g); - if (v == graph_traits::null_vertex()) - continue; - - double k1 = _deg(v, g); - typename graph_traits::out_edge_iterator e, e_end; - for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) - { - double k2 = _deg(target(*e,g),g); - sa[k1]++; - sb[k2]++; - e_xy += k1*k2; - n_edges++; - } - } - - sa.Gather(); - sb.Gather(); - - double t1 = e_xy/n_edges; - double avg_a = GetHistogramMean(a), avg_b = GetHistogramMean(b); - double da = GetHistogramDeviation(a,avg_a), db = - GetHistogramDeviation(b,avg_b); - - if (da*db > 0) - r = (t1 - avg_a*avg_b)/(da*db); - else - r = (t1 - avg_a*avg_b); - - // "jackknife" variance - r_err = 0.0; - double diff_a = 0.0, diff_b = 0.0; - for (typeof(a.begin()) iter = a.begin(); iter != a.end(); ++iter) - diff_a += (iter->first - avg_a)*iter->second; - for (typeof(b.begin()) iter = b.begin(); iter != b.end(); ++iter) - diff_b += (iter->first - avg_b)*iter->second; - - double err = 0.0; - #pragma omp parallel for default(shared) private(i) schedule(dynamic)\ - reduction(+:err) - for (i = 0; i < N; ++i) - { - typename graph_traits::vertex_descriptor v = vertex(i, g); - if (v == graph_traits::null_vertex()) - continue; - - double k1 = _deg(v, g); - typename graph_traits::out_edge_iterator e, e_end; - for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) - { - double k2 = _deg(target(*e, g), g); - double t1l = (e_xy - k1*k2)/(n_edges-1); - double avg_al = (avg_a*n_edges - k1)/(n_edges-1); - double avg_bl = (avg_b*n_edges - k2)/(n_edges-1); - double dal = da - 2*diff_a*(avg_al-avg_a) + - (avg_al-avg_a)*(avg_al-avg_a); - double dbl = db - 2*diff_b*(avg_bl-avg_b) + - (avg_bl-avg_b)*(avg_bl-avg_b); - double rl; - if (dal*dbl > 0) - rl = (t1l - avg_al*avg_bl)/(dal*dbl); - else - rl = (t1l - avg_al*avg_bl); - err += (r-rl)*(r-rl); - } - } - r_err = sqrt(err); - } - - DegreeSelector& _deg; -}; - -struct choose_scalar_assortativity_coefficient -{ - choose_scalar_assortativity_coefficient(const GraphInterface& g, - GraphInterface::deg_t deg, - double& a, double& a_err) - : _g(g), _a(a), _a_err(a_err) - { - tie(_deg, _deg_name) = get_degree_type(deg); - } - - - template - void operator()(DegreeSelector) - { - if (mpl::at::type::value == _deg) - { - DegreeSelector deg(_deg_name, _g, true); - run_action(_g, bind(get_scalar_assortativity_coefficient - (deg), - _1, var(_a), var(_a_err))); - } - } - - const GraphInterface& _g; - double& _a; - double& _a_err; - GraphInterface::degree_t _deg; - string _deg_name; -}; - - pair GraphInterface::GetScalarAssortativityCoefficient(GraphInterface::deg_t deg) const @@ -297,10 +56,10 @@ GraphInterface::GetScalarAssortativityCoefficient(GraphInterface::deg_t deg) double a, a_err; try { - typedef mpl::vector - degrees; - mpl::for_each - (choose_scalar_assortativity_coefficient(*this,deg, a, a_err)); + run_action<>()(*this, bind(get_scalar_assortativity_coefficient(), + _1, _2, var(a), var(a_err)), + all_selectors()) + (degree_selector(deg, _properties)); } catch (dynamic_get_failure &e) { diff --git a/src/graph/graph_assortativity.hh b/src/graph/graph_assortativity.hh new file mode 100644 index 00000000..e91cf8bc --- /dev/null +++ b/src/graph/graph_assortativity.hh @@ -0,0 +1,199 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2007 Tiago de Paula Peixoto +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef GRAPH_ASSORTATIVITY_HH +#define GRAPH_ASSORTATIVITY_HH + +#include +#include "shared_map.hh" + +namespace graph_tool +{ +using namespace std; +using namespace boost; + +// this will calculate the assortativity coefficient, based on the property +// pointed by 'deg' + +struct get_assortativity_coefficient +{ + template + void operator()(const Graph* gp, DegreeSelector deg, double& r, + double& r_err) const + { + const Graph& g = *gp; + size_t n_edges = 0; + int e_kk = 0; + tr1::unordered_map a, b; + SharedMap > sa(a), sb(b); + + int i, N = num_vertices(g); + #pragma omp parallel for default(shared) private(i) firstprivate(sa,sb)\ + schedule(dynamic) reduction(+:e_kk, n_edges) + for (i = 0; i < N; ++i) + { + typename graph_traits::vertex_descriptor v = vertex(i, g); + if (v == graph_traits::null_vertex()) + continue; + + double k1 = deg(v, g); + typename graph_traits::out_edge_iterator e, e_end; + for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) + { + double k2 = deg(target(*e, g), g); + sa[k1]++; + sb[k2]++; + if (k1 == k2) + e_kk++; + n_edges++; + } + } + + sa.Gather(); + sb.Gather(); + + double t1=double(e_kk)/n_edges, t2=0.0; + + for (typeof(a.begin()) iter = a.begin(); iter != a.end(); ++iter) + if (b.find(iter->second) != b.end()) + t2 += double(iter->second * b[iter->first]); + t2 /= n_edges*n_edges; + + r = (t1 - t2)/(1.0 - t2); + + // "jackknife" variance + double err = 0.0; + #pragma omp parallel for default(shared) private(i) schedule(dynamic)\ + reduction(+:err) + for (i = 0; i < N; ++i) + { + typename graph_traits::vertex_descriptor v = vertex(i, g); + if (v == graph_traits::null_vertex()) + continue; + + double k1 = deg(v, g); + typename graph_traits::out_edge_iterator e, e_end; + for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) + { + double k2 = deg(target(*e,g),g); + double tl2 = (t2*(n_edges*n_edges) - b[k1] - a[k2])/ + ((n_edges-1)*(n_edges-1)); + double tl1 = t1*n_edges; + if (k1 == k2) + tl1 -= 1; + tl1 /= n_edges - 1; + double rl = (tl1 - tl2)/(1.0 - tl2); + err += (r-rl)*(r-rl); + } + } + r_err = sqrt(err); + } +}; + +// this will calculate the _scalar_ assortativity coefficient, based on the +// scalar property pointed by 'deg' + +struct get_scalar_assortativity_coefficient +{ + template + void operator()(const Graph* gp, DegreeSelector deg, double& r, + double& r_err) const + { + const Graph& g = *gp; + size_t n_edges = 0; + double e_xy = 0.0; + tr1::unordered_map a, b; + SharedMap > sa(a), sb(b); + + int i, N = num_vertices(g); + #pragma omp parallel for default(shared) private(i) firstprivate(sa,sb)\ + schedule(dynamic) reduction(+:e_xy, n_edges) + for (i = 0; i < N; ++i) + { + typename graph_traits::vertex_descriptor v = vertex(i, g); + if (v == graph_traits::null_vertex()) + continue; + + double k1 = deg(v, g); + typename graph_traits::out_edge_iterator e, e_end; + for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) + { + double k2 = deg(target(*e,g),g); + sa[k1]++; + sb[k2]++; + e_xy += k1*k2; + n_edges++; + } + } + + sa.Gather(); + sb.Gather(); + + double t1 = e_xy/n_edges; + double avg_a = GetHistogramMean(a), avg_b = GetHistogramMean(b); + double da = GetHistogramDeviation(a,avg_a), db = + GetHistogramDeviation(b,avg_b); + + if (da*db > 0) + r = (t1 - avg_a*avg_b)/(da*db); + else + r = (t1 - avg_a*avg_b); + + // "jackknife" variance + r_err = 0.0; + double diff_a = 0.0, diff_b = 0.0; + for (typeof(a.begin()) iter = a.begin(); iter != a.end(); ++iter) + diff_a += (iter->first - avg_a)*iter->second; + for (typeof(b.begin()) iter = b.begin(); iter != b.end(); ++iter) + diff_b += (iter->first - avg_b)*iter->second; + + double err = 0.0; + #pragma omp parallel for default(shared) private(i) schedule(dynamic)\ + reduction(+:err) + for (i = 0; i < N; ++i) + { + typename graph_traits::vertex_descriptor v = vertex(i, g); + if (v == graph_traits::null_vertex()) + continue; + + double k1 = deg(v, g); + typename graph_traits::out_edge_iterator e, e_end; + for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e) + { + double k2 = deg(target(*e, g), g); + double t1l = (e_xy - k1*k2)/(n_edges-1); + double avg_al = (avg_a*n_edges - k1)/(n_edges-1); + double avg_bl = (avg_b*n_edges - k2)/(n_edges-1); + double dal = da - 2*diff_a*(avg_al-avg_a) + + (avg_al-avg_a)*(avg_al-avg_a); + double dbl = db - 2*diff_b*(avg_bl-avg_b) + + (avg_bl-avg_b)*(avg_bl-avg_b); + double rl; + if (dal*dbl > 0) + rl = (t1l - avg_al*avg_bl)/(dal*dbl); + else + rl = (t1l - avg_al*avg_bl); + err += (r-rl)*(r-rl); + } + } + r_err = sqrt(err); + } +}; + +} // graph_tool namespace + +#endif //GRAPH_ASSORTATIVITY_HH diff --git a/src/graph/graph_betweenness.cc b/src/graph/graph_betweenness.cc index f04b25c5..76d0fc9e 100644 --- a/src/graph/graph_betweenness.cc +++ b/src/graph/graph_betweenness.cc @@ -15,31 +15,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include "graph_filtering.hh" -#include #include -#include +#include #include "graph.hh" -#include "histogram.hh" -#include "graph_filtering.hh" #include "graph_selectors.hh" -#include "graph_properties.hh" -#include "graph_adaptor.hh" - -#include - using namespace std; using namespace boost; using namespace boost::lambda; using namespace graph_tool; -template -void normalize_betweenness(const Graph& g, VertexBetweenness vertex_betweenness, - EdgeBetweenness edge_betweenness) +template +void normalize_betweenness(const Graph& g, + EdgeBetweenness edge_betweenness, + VertexBetweenness vertex_betweenness) { - size_t n = HardNumVertices()(g); + size_t n = HardNumVertices()(&g); double factor = 2.0/(n*n - 3*n + 2); typename graph_traits::vertex_iterator v, v_end; @@ -59,170 +53,147 @@ struct get_betweenness { template - void operator()(Graph& g, VertexIndexMap index_map, + void operator()(const Graph* gp, VertexIndexMap index_map, EdgeBetweenness edge_betweenness, VertexBetweenness vertex_betweenness) const { - brandes_betweenness_centrality(g, vertex_index_map(index_map). - edge_centrality_map(edge_betweenness). - centrality_map(vertex_betweenness)); - normalize_betweenness(g, vertex_betweenness, edge_betweenness); + vector::edge_descriptor> > + incoming_map(num_vertices(*gp)); + vector distance_map(num_vertices(*gp)); + vector::value_type> + dependency_map(num_vertices(*gp)); + vector path_count_map(num_vertices(*gp)); + + brandes_betweenness_centrality + (*gp, vertex_betweenness,edge_betweenness, + make_iterator_property_map(incoming_map.begin(), index_map), + make_iterator_property_map(distance_map.begin(), index_map), + make_iterator_property_map(dependency_map.begin(), index_map), + make_iterator_property_map(path_count_map.begin(), index_map), + index_map); + normalize_betweenness(*gp, edge_betweenness, vertex_betweenness); } }; -template +template struct get_weighted_betweenness { - get_weighted_betweenness(Graph& g, VertexIndexMap vertex_index, - EdgeIndexMap edge_index, string& weight, - dynamic_properties& dp, - EdgeBetweenness edge_betweenness, - VertexBetweenness vertex_betweenness, bool& found) - :_g(g), _vertex_index(vertex_index), _edge_index(edge_index), - _weight(weight), _dp(dp), _edge_betweenness(edge_betweenness), - _vertex_betweenness(vertex_betweenness), _found(found) {} - - template - void operator()(WeightType) - { - try - { - typedef vector_property_map weight_map_t; - weight_map_t weight_map(_edge_index); - weight_map = get_static_property_map - (find_property_map(_dp, _weight, - typeid(GraphInterface::edge_t))); - brandes_betweenness_centrality - (_g, vertex_index_map(_vertex_index).weight_map(weight_map). - edge_centrality_map(_edge_betweenness). - centrality_map(_vertex_betweenness)); - normalize_betweenness(_g, _vertex_betweenness, _edge_betweenness); - _found = true; - } - catch (property_not_found) {} - catch (bad_cast) {} - } - - Graph& _g; - VertexIndexMap _vertex_index; - EdgeIndexMap _edge_index; - string& _weight; - dynamic_properties& _dp; - EdgeBetweenness _edge_betweenness; - VertexBetweenness _vertex_betweenness; - bool& _found; -}; + get_weighted_betweenness(VertexIndexMap index): _vertex_index(index) {} -struct choose_weight_map -{ - template - void operator()(Graph& g, VertexIndexMap vertex_index, - EdgeIndexMap edge_index, string& weight, - dynamic_properties& dp, - EdgeBetweenness edge_betweenness, - VertexBetweenness vertex_betweenness, bool& found) const + template + void operator()(const Graph* gp, EdgeBetweenness edge_betweenness, + VertexBetweenness vertex_betweenness, + WeightMap weight_map) const { - mpl::for_each - (get_weighted_betweenness - (g, vertex_index, edge_index, weight, dp, edge_betweenness, - vertex_betweenness, found)); + vector::edge_descriptor> > + incoming_map(num_vertices(*gp)); + vector::value_type> + distance_map(num_vertices(*gp)); + vector::value_type> + dependency_map(num_vertices(*gp)); + vector path_count_map(num_vertices(*gp)); + + brandes_betweenness_centrality + (*gp, vertex_betweenness,edge_betweenness, + make_iterator_property_map(incoming_map.begin(), _vertex_index), + make_iterator_property_map(distance_map.begin(), _vertex_index), + make_iterator_property_map(dependency_map.begin(), _vertex_index), + make_iterator_property_map(path_count_map.begin(), _vertex_index), + _vertex_index, weight_map); + normalize_betweenness(*gp, edge_betweenness, vertex_betweenness); } + + VertexIndexMap _vertex_index; }; void GraphInterface::GetBetweenness(string weight, string edge_betweenness, string vertex_betweenness) { - typedef vector_property_map - vertex_betweenness_map_t; - vertex_betweenness_map_t vertex_betweenness_map(_vertex_index); - - typedef vector_property_map - edge_betweenness_map_t; - edge_betweenness_map_t edge_betweenness_map(_edge_index); + boost::any edge_prop, vertex_prop; - if (weight != "") + try { - bool found = false; - run_action(*this, bind(choose_weight_map(), _1, - var(_vertex_index), var(_edge_index), - var(weight), var(_properties), - var(edge_betweenness_map), - var(vertex_betweenness_map), - var(found))); - if (!found) - throw GraphException("error getting scalar property: " + weight); + find_property_map(_properties, edge_betweenness, typeid(edge_t)); + edge_prop = prop(edge_betweenness, _edge_index, _properties); + if (!belongs()(edge_prop)) + throw GraphException("edge property " + edge_betweenness + + " is not of floating type"); } - else + catch (property_not_found) { - run_action(*this, bind(get_betweenness(), _1, - var(_vertex_index), - var(edge_betweenness_map), - var(vertex_betweenness_map)), - reverse_check(), directed_check()); - } - - if (vertex_betweenness != "") - { - try - { - find_property_map(_properties, vertex_betweenness, - typeid(vertex_t)); - RemoveVertexProperty(vertex_betweenness); - } - catch (property_not_found) {} + typedef vector_property_map + edge_betweenness_map_t; + edge_betweenness_map_t edge_betweenness_map(_edge_index); + edge_prop = edge_betweenness_map; + } - _properties.property(vertex_betweenness, vertex_betweenness_map); + try + { + find_property_map(_properties, vertex_betweenness, typeid(vertex_t)); + vertex_prop = prop(vertex_betweenness, _vertex_index, _properties); + if (!belongs()(vertex_prop)) + throw GraphException("vertex property " + vertex_betweenness + + " is not of floating type"); + } + catch (property_not_found) + { + typedef vector_property_map + vertex_betweenness_map_t; + vertex_betweenness_map_t vertex_betweenness_map(_vertex_index); + vertex_prop = vertex_betweenness_map; } - if (edge_betweenness != "") + if (weight != "") { try { - find_property_map(_properties, edge_betweenness, - typeid(edge_t)); - RemoveEdgeProperty(edge_betweenness); + run_action<>() + (*this, + get_weighted_betweenness(_vertex_index), + edge_floating_properties(), vertex_floating_properties(), + edge_scalar_properties())(edge_prop, vertex_prop, + prop(weight, _edge_index, + _properties)); + } + catch (property_not_found& e) + { + throw GraphException("edge property " + weight + " not found"); } - catch (property_not_found) {} - - _properties.property(edge_betweenness, edge_betweenness_map); } - + else + { + run_action<>()(*this, bind(get_betweenness(), _1, + _vertex_index, _2, _3), + edge_floating_properties(), vertex_floating_properties()) + (edge_prop, vertex_prop); + } } struct get_central_point_dominance { template - void operator()(Graph& g, VertexBetweenness vertex_betweenness, double& c) + void operator()(Graph* g, VertexBetweenness vertex_betweenness, double& c) const { - c = central_point_dominance(g, vertex_betweenness); + c = central_point_dominance(*g, vertex_betweenness); } }; - double GraphInterface::GetCentralPointDominance(string vertex_betweenness) { try { - typedef DynamicPropertyMapWrap betweenness_map_t; - betweenness_map_t betweenness(find_property_map(_properties, - vertex_betweenness, - typeid(vertex_t))); double c = 0.0; - bool reversed = this->GetReversed(); bool directed = this->GetDirected(); + bool reversed = this->GetReversed(); this->SetReversed(false); this->SetDirected(true); - run_action(*this, bind(get_central_point_dominance(), _1, - var(betweenness), var(c)), - never_reversed(), always_directed()); + run_action() + (*this, bind(get_central_point_dominance(), _1, _2, + var(c)), vertex_scalar_properties()) + (prop(vertex_betweenness, _vertex_index, _properties)); this->SetReversed(reversed); this->SetDirected(directed); return c; diff --git a/src/graph/graph_bind.cc b/src/graph/graph_bind.cc index 3810d34b..2c221cb5 100644 --- a/src/graph/graph_bind.cc +++ b/src/graph/graph_bind.cc @@ -16,6 +16,7 @@ // along with this program. If not, see . +#include "graph_filtering.hh" #include #include #include "graph.hh" @@ -174,56 +175,13 @@ struct hist_to_dict } }; -struct pos_t_to_tuple -{ - static PyObject* convert(const pos_t& p) - { - boost::python::tuple t = boost::python::make_tuple(p.x,p.y); - return incref(t.ptr()); - } -}; - -struct pos_t_from_tuple -{ - pos_t_from_tuple() - { - converter::registry::push_back(&convertible, &construct, - boost::python::type_id()); - } - - static void* convertible(PyObject* obj_ptr) - { - handle<> x(borrowed(obj_ptr)); - object o(x); - extract first(o[0]); - extract second(o[1]); - if (!first.check() || !second.check()) - return 0; - return obj_ptr; - } - - static void construct(PyObject* obj_ptr, - converter::rvalue_from_python_stage1_data* data) - { - handle<> x(borrowed(obj_ptr)); - object o(x); - pos_t value; - value.x = extract(o[0]); - value.y = extract(o[1]); - void* storage = - ( (boost::python::converter::rvalue_from_python_storage - *) data)->storage.bytes; - new (storage) pos_t(value); - data->convertible = storage; - } -}; - struct LibInfo { string GetName() const {return PACKAGE_NAME;} string GetAuthor() const {return AUTHOR;} string GetCopyright() const {return COPYRIGHT;} - string GetVersion() const {return VERSION " (commit " GIT_COMMIT ", " GIT_COMMIT_DATE ")";} + string GetVersion() const {return VERSION " (commit " GIT_COMMIT + ", " GIT_COMMIT_DATE ")";} string GetLicense() const {return "GPL version 3 or above";} string GetCXXFLAGS() const {return CXXFLAGS;} string GetInstallPrefix() const {return INSTALL_PREFIX;} @@ -293,15 +251,13 @@ BOOST_PYTHON_MODULE(libgraph_tool) .def("GetReversed", &GraphInterface::GetReversed) .def("SetVertexFilterProperty", &GraphInterface::SetVertexFilterProperty) - .def("SetVertexFilterRange", &GraphInterface::SetVertexFilterRange) .def("IsVertexFilterActive", &GraphInterface::IsVertexFilterActive) .def("SetEdgeFilterProperty", &GraphInterface::SetEdgeFilterProperty) - .def("SetEdgeFilterRange", &GraphInterface::SetEdgeFilterRange) .def("IsEdgeFilterActive", &GraphInterface::IsEdgeFilterActive) - .def("EditEdgeProperty", &GraphInterface::EditEdgeProperty) - .def("EditVertexProperty", &GraphInterface::EditVertexProperty) - .def("EditGraphProperty", &GraphInterface::EditGraphProperty) + .def("AddEdgeProperty", &GraphInterface::AddEdgeProperty) + .def("AddVertexProperty", &GraphInterface::AddVertexProperty) + .def("AddGraphProperty", &GraphInterface::AddGraphProperty) .def("RemoveEdgeProperty", &GraphInterface::RemoveEdgeProperty) .def("RemoveVertexProperty", &GraphInterface::RemoveVertexProperty) .def("RemoveGraphProperty", &GraphInterface::RemoveGraphProperty) @@ -320,11 +276,13 @@ BOOST_PYTHON_MODULE(libgraph_tool) .def("ReadFromFile", ReadFromFile1) .def("ReadFromFile", ReadFromFile2) .def("Vertices", &GraphInterface::Vertices) + .def("Vertex", &GraphInterface::Vertex) .def("Edges", &GraphInterface::Edges) .def("AddVertex", &GraphInterface::AddVertex) .def("AddEdge", &GraphInterface::AddEdge) .def("RemoveVertex", &GraphInterface::RemoveVertex) .def("RemoveEdge", &GraphInterface::RemoveEdge) + .def("Clear", &GraphInterface::Clear) .def("GetVertexProperties", &GraphInterface::GetVertexProperties) .def("GetEdgeProperties", &GraphInterface::GetEdgeProperties) .def("GetGraphProperties", &GraphInterface::GetGraphProperties) @@ -347,8 +305,6 @@ BOOST_PYTHON_MODULE(libgraph_tool) to_python_converter, tuple_to_tuple >(); tuple_from_tuple(); - to_python_converter(); - pos_t_from_tuple(); pair_from_tuple(); to_python_converter >(); to_python_converter >(); diff --git a/src/graph/graph_clustering.cc b/src/graph/graph_clustering.cc index 44dc95d4..470bdbd0 100644 --- a/src/graph/graph_clustering.cc +++ b/src/graph/graph_clustering.cc @@ -15,204 +15,54 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . - -#include -#include -#include -#include -#include -#include +#include "graph_filtering.hh" #include "graph.hh" -#include "histogram.hh" -#include "graph_filtering.hh" #include "graph_selectors.hh" #include "graph_properties.hh" +#include "graph_clustering.hh" + using namespace std; using namespace boost; using namespace boost::lambda; using namespace graph_tool; -// calculates the number of triangles to which v belongs -template -pair -get_triangles(typename graph_traits::vertex_descriptor v, const Graph &g) -{ - tr1::unordered_set::vertex_descriptor> - neighbour_set1, neighbour_set2, neighbour_set3; - - size_t triangles = 0, k = 0; - - typename graph_traits::adjacency_iterator n1_begin, n1_end, n1; - tie(n1_begin, n1_end) = adjacent_vertices(v, g); - for (n1 = n1_begin; n1 != n1_end; ++n1) - { - if (*n1 == v) // no self-loops - continue; - if (neighbour_set1.find(*n1) != neighbour_set1.end()) - continue; - else - neighbour_set1.insert(*n1); - - typename graph_traits::adjacency_iterator n2_begin, n2_end, n2; - tie(n2_begin, n2_end) = adjacent_vertices(*n1, g); - for (n2 = n2_begin; n2 != n2_end; ++n2) - { - if (*n2 == *n1) // no self-loops - continue; - if (neighbour_set2.find(*n2) != neighbour_set2.end()) - continue; - else - neighbour_set2.insert(*n2); - - typename graph_traits::adjacency_iterator - n3_begin, n3_end, n3; - tie(n3_begin, n3_end) = adjacent_vertices(*n2, g); - for (n3 = n3_begin; n3 != n3_end; ++n3) - { - if (*n3 == *n2) // no self-loops - continue; - if (neighbour_set3.find(*n3) != neighbour_set3.end()) - continue; - else - neighbour_set3.insert(*n3); - - if (*n3 == v) //found a triangle - triangles++; - } - neighbour_set3.clear(); - } - neighbour_set2.clear(); - k++; - } - neighbour_set1.clear(); - return make_pair(triangles/2,(k*(k-1))/2); -} - - -// retrieves the global clustering coefficient -struct get_global_clustering -{ - template - void operator()(const Graph &g, double &c, double &c_err) const - { - size_t triangles = 0, n = 0; - pair temp; - - int i, N = num_vertices(g); - - #pragma omp parallel for default(shared) private(i,temp) \ - schedule(dynamic) - for (i = 0; i < N; ++i) - { - typename graph_traits::vertex_descriptor v = vertex(i, g); - if (v == graph_traits::null_vertex()) - continue; - - temp = get_triangles(v, g); - - #pragma omp critical - { - triangles += temp.first; - n += temp.second; - } - } - c = double(triangles)/n; - - // "jackknife" variance - c_err = 0.0; - - #pragma omp parallel for default(shared) private(i,temp) \ - schedule(dynamic) - for (i = 0; i < N; ++i) - { - typename graph_traits::vertex_descriptor v = vertex(i, g); - if (v == graph_traits::null_vertex()) - continue; - - temp = get_triangles(v, g); - double cl = double(triangles - temp.first)/(n - temp.second); - - #pragma omp atomic - c_err += (c - cl)*(c - cl); - } - c_err = sqrt(c_err); - } -}; - - pair GraphInterface::GetGlobalClustering() { double c, c_err; bool directed = _directed; _directed = false; - run_action(*this, bind(get_global_clustering(), _1, var(c), - var(c_err)), - reverse_check(), always_undirected()); + run_action() + (*this, bind(get_global_clustering(), _1, var(c), var(c_err)))(); _directed = directed; return make_pair(c,c_err); } -// sets the local clustering coefficient to a property -struct set_clustering_to_property -{ - template - void operator()(const Graph& g, ClustMap& clust_map) const - { - typename get_undirected_graph::type ug(g); - int i, N = num_vertices(g); - - #pragma omp parallel for default(shared) private(i) schedule(dynamic) - for (i = 0; i < N; ++i) - { - typename graph_traits::vertex_descriptor v = vertex(i, g); - if (v == graph_traits::null_vertex()) - continue; - - pair triangles = get_triangles(v,ug); // get from ug - double clustering = (triangles.second > 0) ? - double(triangles.first)/triangles.second : - 0.0; - - #pragma omp critical - { - clust_map[v] = clustering; - } - } - } - - template - struct get_undirected_graph - { - typedef typename mpl::if_ - < is_convertible::directed_category, - directed_tag>, - const UndirectedAdaptor, - const Graph& >::type type; - }; -}; - - void GraphInterface::SetLocalClusteringToProperty(string property) { - typedef vector_property_map clust_map_t; - clust_map_t clust_map(num_vertices(_mg), _vertex_index); - - bool directed = _directed; - _directed = false; - run_action(*this, bind(set_clustering_to_property(), _1, - var(clust_map)), - reverse_check(), always_undirected()); - _directed = directed; + boost::any vertex_prop; try { find_property_map(_properties, property, typeid(vertex_t)); - RemoveVertexProperty(property); + vertex_prop = prop(property, _vertex_index, _properties); + if (!belongs()(vertex_prop)) + throw GraphException("vertex property " + property + + " is not of floating type"); + } + catch (property_not_found) + { + typedef vector_property_map clust_map_t; + clust_map_t clust_map(num_vertices(_mg), _vertex_index); + _properties.property(property, clust_map); + vertex_prop = clust_map; } - catch (property_not_found) {} - _properties.property(property, clust_map); + bool directed = _directed; + _directed = false; + run_action() + (*this, bind(set_clustering_to_property(), _1, _2))(vertex_prop); + _directed = directed; } diff --git a/src/graph/graph_clustering.hh b/src/graph/graph_clustering.hh new file mode 100644 index 00000000..de6ce48c --- /dev/null +++ b/src/graph/graph_clustering.hh @@ -0,0 +1,174 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2007 Tiago de Paula Peixoto +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef GRAPH_CLUSTERING_HH +#define GRAPH_CLUSTERING_HH + +#include +#include + +namespace graph_tool +{ +using namespace boost; + +// calculates the number of triangles to which v belongs +template +pair +get_triangles(typename graph_traits::vertex_descriptor v, const Graph &g) +{ + tr1::unordered_set::vertex_descriptor> + neighbour_set1, neighbour_set2, neighbour_set3; + + size_t triangles = 0, k = 0; + + typename graph_traits::adjacency_iterator n1_begin, n1_end, n1; + tie(n1_begin, n1_end) = adjacent_vertices(v, g); + for (n1 = n1_begin; n1 != n1_end; ++n1) + { + if (*n1 == v) // no self-loops + continue; + if (neighbour_set1.find(*n1) != neighbour_set1.end()) + continue; + else + neighbour_set1.insert(*n1); + + typename graph_traits::adjacency_iterator n2_begin, n2_end, n2; + tie(n2_begin, n2_end) = adjacent_vertices(*n1, g); + for (n2 = n2_begin; n2 != n2_end; ++n2) + { + if (*n2 == *n1) // no self-loops + continue; + if (neighbour_set2.find(*n2) != neighbour_set2.end()) + continue; + else + neighbour_set2.insert(*n2); + + typename graph_traits::adjacency_iterator + n3_begin, n3_end, n3; + tie(n3_begin, n3_end) = adjacent_vertices(*n2, g); + for (n3 = n3_begin; n3 != n3_end; ++n3) + { + if (*n3 == *n2) // no self-loops + continue; + if (neighbour_set3.find(*n3) != neighbour_set3.end()) + continue; + else + neighbour_set3.insert(*n3); + + if (*n3 == v) //found a triangle + triangles++; + } + neighbour_set3.clear(); + } + neighbour_set2.clear(); + k++; + } + neighbour_set1.clear(); + return make_pair(triangles/2,(k*(k-1))/2); +} + + +// retrieves the global clustering coefficient +struct get_global_clustering +{ + template + void operator()(const Graph* gp, double& c, double& c_err) const + { + const Graph& g = *gp; + size_t triangles = 0, n = 0; + pair temp; + + int i, N = num_vertices(g); + + #pragma omp parallel for default(shared) private(i,temp) \ + schedule(dynamic) reduction(+:triangles) + for (i = 0; i < N; ++i) + { + typename graph_traits::vertex_descriptor v = vertex(i, g); + if (v == graph_traits::null_vertex()) + continue; + + temp = get_triangles(v, g); + triangles += temp.first; + n += temp.second; + } + c = double(triangles)/n; + + // "jackknife" variance + c_err = 0.0; + + #pragma omp parallel for default(shared) private(i,temp) \ + schedule(dynamic) reduction(+|c_err) + for (i = 0; i < N; ++i) + { + typename graph_traits::vertex_descriptor v = vertex(i, g); + if (v == graph_traits::null_vertex()) + continue; + + temp = get_triangles(v, g); + double cl = double(triangles - temp.first)/(n - temp.second); + + c_err += (c - cl)*(c - cl); + } + c_err = sqrt(c_err); + } +}; + + +// sets the local clustering coefficient to a property +struct set_clustering_to_property +{ + template + void operator()(const Graph* gp, ClustMap clust_map) const + { + const Graph& g = *gp; + typename get_undirected_graph::type ug(g); + int i, N = num_vertices(g); + + #pragma omp parallel for default(shared) private(i) schedule(dynamic) + for (i = 0; i < N; ++i) + { + typename graph_traits::vertex_descriptor v = vertex(i, g); + if (v == graph_traits::null_vertex()) + continue; + + pair triangles = get_triangles(v,ug); // get from ug + double clustering = (triangles.second > 0) ? + double(triangles.first)/triangles.second : + 0.0; + + #pragma omp critical + { + clust_map[v] = clustering; + } + } + } + + template + struct get_undirected_graph + { + typedef typename mpl::if_ + < is_convertible::directed_category, + directed_tag>, + const UndirectedAdaptor, + const Graph& >::type type; + }; +}; + +} //graph-tool namespace + +#endif // GRAPH_CLUSTERING_HH diff --git a/src/graph/graph_community.cc b/src/graph/graph_community.cc index ee9057a6..a48b5114 100644 --- a/src/graph/graph_community.cc +++ b/src/graph/graph_community.cc @@ -15,675 +15,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . - -#include -#include -#include -#include -#include -#include - -#include "graph.hh" -#include "histogram.hh" #include "graph_filtering.hh" +#include "graph.hh" #include "graph_selectors.hh" #include "graph_properties.hh" +#include +#include + +#include "graph_community.hh" + using namespace std; using namespace boost; using namespace boost::lambda; using namespace graph_tool; -using std::tr1::unordered_map; -using std::tr1::unordered_set; - -typedef boost::mt19937 rng_t; - -template -size_t out_degree_no_loops(typename graph_traits::vertex_descriptor v, - const Graph &g) -{ - size_t k = 0; - typename graph_traits::adjacency_iterator a,a_end; - for (tie(a,a_end) = adjacent_vertices(v,g); a != a_end; ++a) - if (*a != v) - k++; - return k; -} - - -// computes the community structure through a spin glass system with -// simulated annealing - -template