Commit 360a3395 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Correlations algorithms refactoring

The whole histogram code has been redone, and the code has been
simplified. The three-point vertex-edge-vertex correlation has been
scrapped, since it's not frequently used, and would make compilation
even more expensive.

This also adds some missing files to the generation routine.
parent 6063ece0
...@@ -205,6 +205,7 @@ Makefile ...@@ -205,6 +205,7 @@ Makefile
src/Makefile src/Makefile
src/graph/Makefile src/graph/Makefile
src/graph/correlations/Makefile src/graph/correlations/Makefile
src/graph/generation/Makefile
src/graph_tool/Makefile src/graph_tool/Makefile
]) ])
## Process this file with automake to produce Makefile.in ## Process this file with automake to produce Makefile.in
#SUBDIRS = correlations SUBDIRS = generation correlations
AM_CPPFLAGS =\ AM_CPPFLAGS =\
-I. -I.. \ -I. -I.. \
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
AM_CPPFLAGS =\ AM_CPPFLAGS =\
-I. -I.. \ -I. -I.. \
-I $(pythondir)/numpy/core/include/numpy/ \
-I../boost-workaround \ -I../boost-workaround \
-DHAVE_CONFIG_H -DHAVE_CONFIG_H
...@@ -12,7 +13,7 @@ AM_CXXFLAGS =\ ...@@ -12,7 +13,7 @@ AM_CXXFLAGS =\
AM_CFLAGS=$(AM_CXXFLAGS) AM_CFLAGS=$(AM_CXXFLAGS)
libgraph_tool_correlationsdir = $(pythondir)/graph_tool libgraph_tool_correlationsdir = $(pythondir)/graph_tool/correlations
libgraph_tool_correlations_LTLIBRARIES = libgraph_tool_correlations.la libgraph_tool_correlations_LTLIBRARIES = libgraph_tool_correlations.la
...@@ -21,33 +22,13 @@ libgraph_tool_correlations_la_includedir = $(pythondir)/graph_tool/include ...@@ -21,33 +22,13 @@ libgraph_tool_correlations_la_includedir = $(pythondir)/graph_tool/include
libgraph_tool_correlations_la_SOURCES = \ libgraph_tool_correlations_la_SOURCES = \
graph_assortativity.cc \ graph_assortativity.cc \
graph_correlations.cc \ graph_correlations.cc \
graph_correlations_combined.cc \
graph_correlations_combined_imp1.cc \
graph_correlations_combined_corr.cc \
graph_correlations_combined_corr_imp1.cc \
graph_correlations_imp1.cc \ graph_correlations_imp1.cc \
graph_correlations_imp2.cc \ graph_correlations_combined.cc \
graph_correlations_imp3.cc \ graph_correlations_bind.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_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
libgraph_tool_correlations_la_include_HEADERS = \ libgraph_tool_correlations_la_include_HEADERS = \
graph_assortativity.hh \ graph_assortativity.hh \
graph_correlations_combined.hh \ graph_correlations.hh
graph_correlations.hh \
graph_correlations_neighbours.hh \
graph_edge_correlations.hh
libgraph_tool_correlations_la_LIBADD = \ libgraph_tool_correlations_la_LIBADD = \
$(PYTHON_LDFLAGS) \ $(PYTHON_LDFLAGS) \
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_filtering.hh" #include "graph_filtering.hh"
#include "graph_selectors.hh" #include "graph_selectors.hh"
#include "graph_properties.hh" #include "graph_properties.hh"
...@@ -24,48 +23,39 @@ ...@@ -24,48 +23,39 @@
#include "graph_assortativity.hh" #include "graph_assortativity.hh"
using namespace std; using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool; using namespace graph_tool;
pair<double,double> pair<double,double>
GraphInterface::GetAssortativityCoefficient(GraphInterface::deg_t deg) const assortativity_coefficient(const GraphInterface& gi,
GraphInterface::deg_t deg)
{ {
using namespace boost::lambda;
double a, a_err; double a, a_err;
try run_action<>()(gi,bind<void>(get_assortativity_coefficient(), _1, _2,
{ var(a), var(a_err)), all_selectors())
run_action<>()(*this, (degree_selector(deg, gi));
bind<void>(get_assortativity_coefficient(), _1, _2,
var(a), var(a_err)), all_selectors())
(degree_selector(deg, _properties));
}
catch (dynamic_get_failure &e)
{
throw GraphException("error getting scalar property: " +
string(e.what()));
}
return make_pair(a, a_err); return make_pair(a, a_err);
} }
pair<double,double> pair<double,double>
GraphInterface::GetScalarAssortativityCoefficient(GraphInterface::deg_t deg) scalar_assortativity_coefficient(const GraphInterface& gi,
const GraphInterface::deg_t deg)
{ {
using namespace boost::lambda;
double a, a_err; double a, a_err;
try run_action<>()(gi, bind<void>(get_scalar_assortativity_coefficient(),
{ _1, _2, var(a), var(a_err)),
run_action<>()(*this, bind<void>(get_scalar_assortativity_coefficient(),
_1, _2, var(a), var(a_err)),
all_selectors()) all_selectors())
(degree_selector(deg, _properties)); (degree_selector(deg, gi));
}
catch (dynamic_get_failure &e)
{
throw GraphException("error getting scalar property: " +
string(e.what()));
}
return make_pair(a, a_err); return make_pair(a, a_err);
} }
#include <boost/python.hpp>
using namespace boost::python;
void export_assortativity()
{
def("assortativity_coefficient", &assortativity_coefficient);
def("scalar_assortativity_coefficient", &scalar_assortativity_coefficient);
}
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <tr1/unordered_set> #include <tr1/unordered_set>
#include "shared_map.hh" #include "shared_map.hh"
#include "histogram.hh"
namespace graph_tool namespace graph_tool
{ {
...@@ -144,9 +145,8 @@ struct get_scalar_assortativity_coefficient ...@@ -144,9 +145,8 @@ struct get_scalar_assortativity_coefficient
sb.Gather(); sb.Gather();
double t1 = e_xy/n_edges; double t1 = e_xy/n_edges;
double avg_a = GetHistogramMean(a), avg_b = GetHistogramMean(b); double avg_a = GetMapMean(a), avg_b = GetMapMean(b);
double da = GetHistogramDeviation(a,avg_a), db = double da = GetMapDeviation(a,avg_a), db = GetMapDeviation(b,avg_b);
GetHistogramDeviation(b,avg_b);
if (da*db > 0) if (da*db > 0)
r = (t1 - avg_a*avg_b)/(da*db); r = (t1 - avg_a*avg_b)/(da*db);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "graph_filtering.hh" #include "graph_filtering.hh"
#include <boost/lambda/bind.hpp> #include <boost/lambda/bind.hpp>
#include <boost/python.hpp>
#include "graph.hh" #include "graph.hh"
#include "histogram.hh" #include "histogram.hh"
...@@ -26,6 +27,8 @@ ...@@ -26,6 +27,8 @@
#include "graph_correlations.hh" #include "graph_correlations.hh"
#include <iostream>
using namespace std; using namespace std;
using namespace boost; using namespace boost;
using namespace boost::lambda; using namespace boost::lambda;
...@@ -33,51 +36,63 @@ using namespace graph_tool; ...@@ -33,51 +36,63 @@ using namespace graph_tool;
// implementations spread across different compile units to minimize memory // implementations spread across different compile units to minimize memory
// usage during compilation // usage during compilation
void graph_correlations_imp1(const GraphInterface& g, hist2d_t& hist, void graph_correlations_imp1(const GraphInterface& g, python::object& hist,
boost::any deg1, boost::any deg2, python::object& ret_bins,
boost::any weight);
void graph_correlations_imp2(const GraphInterface& g, hist2d_t& hist,
boost::any deg1, boost::any deg2, boost::any deg1, boost::any deg2,
boost::any weight); boost::any weight,
void graph_correlations_imp3(const GraphInterface& g, hist2d_t& hist, const array<vector<long double>,2>& bins);
boost::any deg1, boost::any deg2,
boost::any weight);
typedef ConstantPropertyMap<int,GraphInterface::edge_t> cweight_map_t; typedef ConstantPropertyMap<int,GraphInterface::edge_t> cweight_map_t;
hist2d_t python::object
GraphInterface::GetVertexCorrelationHistogram(GraphInterface::deg_t deg1, get_vertex_correlation_histogram(const GraphInterface& gi,
GraphInterface::deg_t deg2, GraphInterface::deg_t deg1,
string weight) const GraphInterface::deg_t deg2,
string weight,
const vector<long double>& xbin,
const vector<long double>& ybin)
{ {
hist2d_t hist; python::object hist;
python::object ret_bins;
try array<vector<long double>,2> bins;
bins[0] = xbin;
bins[1] = ybin;
any weight_prop;
typedef DynamicPropertyMapWrap<long double, GraphInterface::edge_t>
wrapped_weight_t;
if (weight != "")
{ {
any weight_prop; dynamic_property_map* map =
if (weight != "") any_cast<dynamic_property_map*>(edge_prop(weight, gi, true));
weight_prop = prop(weight, _edge_index, _properties); weight_prop = wrapped_weight_t(*map);
else }
weight_prop = cweight_map_t(1); else
weight_prop = cweight_map_t(1);
run_action<>()(*this, get_correlation_histogram<hist2d_t>(hist), try
{
run_action<>()(gi, get_correlation_histogram<GetNeighboursPairs>
(hist, bins, ret_bins),
all_selectors(), all_selectors(), all_selectors(), all_selectors(),
mpl::vector<cweight_map_t>()) mpl::vector<cweight_map_t>())
(degree_selector(deg1, _properties), (degree_selector(deg1, gi), degree_selector(deg2, gi), weight_prop);
degree_selector(deg2, _properties), weight);
graph_correlations_imp1(*this, hist, degree_selector(deg1, _properties),
degree_selector(deg2, _properties), weight);
graph_correlations_imp2(*this, hist, degree_selector(deg1, _properties),
degree_selector(deg2, _properties), weight);
graph_correlations_imp3(*this, hist, degree_selector(deg1, _properties),
degree_selector(deg2, _properties), weight);
} }
catch (dynamic_get_failure &e) catch (ActionNotFound&)
{ {
throw GraphException("error getting scalar property: " + graph_correlations_imp1(gi, hist, ret_bins, degree_selector(deg1, gi),
string(e.what())); degree_selector(deg2, gi), weight_prop, bins);
} }
return hist; return python::make_tuple(hist, ret_bins);
}
using namespace boost::python;
void export_vertex_correlations()
{
def("vertex_correlation_histogram", &get_vertex_correlation_histogram);
} }
...@@ -19,7 +19,12 @@ ...@@ -19,7 +19,12 @@
#define GRAPH_CORRELATIONS_HH #define GRAPH_CORRELATIONS_HH
#include <algorithm> #include <algorithm>
#include <tr1/unordered_set> #include <boost/numeric/conversion/bounds.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/python/object.hpp>
#include <boost/python/list.hpp>
#include <boost/python/extract.hpp>
#include "numpy_bind.hh"
#include "shared_map.hh" #include "shared_map.hh"
namespace graph_tool namespace graph_tool
...@@ -28,12 +33,85 @@ using namespace std; ...@@ -28,12 +33,85 @@ using namespace std;
using namespace boost; using namespace boost;
using namespace boost::lambda; using namespace boost::lambda;
// retrieves the generalized vertex-vertex correlation histogram // get degrees pairs from source and of neighbours
class GetNeighboursPairs
{
public:
template <class Graph, class Deg1, class Deg2, class Hist, class WeightMap>
void operator()(typename graph_traits<Graph>::vertex_descriptor v,
Deg1& deg1, Deg2& deg2, Graph& g, WeightMap& weight,
Hist& hist)
{
typename Hist::point_t k;
k[0] = deg1(v, g);
typename graph_traits<Graph>::out_edge_iterator e, e_end;
for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e)
{
k[1] = deg2(target(*e,g),g);
hist.PutValue(k, get(weight, *e));
}
}
};
// get degrees pairs from one single vertex
class GetCombinedPair
{
public:
template <class Graph, class Deg1, class Deg2, class Hist, class Dummy>
void operator()(typename graph_traits<Graph>::vertex_descriptor v,
Deg1& deg1, Deg2& deg2, Graph& g, const Dummy&,
Hist& hist)
{
typename Hist::point_t k;
k[0] = deg1(v, g);
k[1] = deg2(v, g);
hist.PutValue(k);
}
};
namespace detail
{
struct select_larger_type
{
template <class Type1, class Type2>
struct apply
{
typedef typename mpl::if_<
typename mpl::greater<typename mpl::sizeof_<Type1>::type,
typename mpl::sizeof_<Type2>::type>::type,
Type1,
Type2>::type type;
};
};
struct select_float_and_larger
{
template <class Type1, class Type2>
struct apply
{
typedef typename mpl::if_<
mpl::equal_to<is_floating_point<Type1>,
is_floating_point<Type2> >,
typename select_larger_type::apply<Type1,Type2>::type,
typename mpl::if_<is_floating_point<Type1>,
Type1,
Type2>::type>::type type;
};
};
}
template <class Hist> // retrieves the generalized vertex-vertex correlation histogram
template <class GetDegreePair>
struct get_correlation_histogram struct get_correlation_histogram
{ {
get_correlation_histogram(Hist& hist): _hist(hist) {} get_correlation_histogram(python::object& hist,
const array<vector<long double>,2>& bins,
python::object& ret_bins)
: _hist(hist), _bins(bins), _ret_bins(ret_bins) {}
template <class Graph, class DegreeSelector1, class DegreeSelector2, template <class Graph, class DegreeSelector1, class DegreeSelector2,
class WeightMap> class WeightMap>
...@@ -41,7 +119,75 @@ struct get_correlation_histogram ...@@ -41,7 +119,75 @@ struct get_correlation_histogram
WeightMap weight) const WeightMap weight) const
{ {
Graph& g = *gp; Graph& g = *gp;
SharedMap<Hist> s_hist(_hist); GetDegreePair put_point;
typedef typename DegreeSelector1::value_type type1;
typedef typename DegreeSelector2::value_type type2;
typedef typename graph_tool::detail::
select_float_and_larger::apply<type1,type2>::type
val_type;
typedef typename property_traits<WeightMap>::value_type count_type;
typedef Histogram<val_type, count_type, 2> hist_t;
array<vector<val_type>,2> bins;
for (size_t i = 0; i < bins.size(); ++i)
{
bins[i].resize(_bins[i].size());
for (size_t j = 0; j < bins[i].size(); ++j)
{
// we'll attempt to recover from out of bounds conditions
try
{
bins[i][j] =
numeric_cast<val_type,long double>(_bins[i][j]);
}
catch (boost::numeric::negative_overflow&)
{
bins[i][j] = boost::numeric::bounds<val_type>::lowest();
}
catch (boost::numeric::positive_overflow&)
{
bins[i][j] = boost::numeric::bounds<val_type>::highest();
}
}
// sort the bins
sort(bins[i].begin(), bins[i].end());
// clean bins of zero size
vector<val_type> temp_bin(1);
temp_bin[0] = bins[i][0];
for (size_t j = 1; j < bins[i].size(); ++j)
{
if (bins[i][j] > bins[i][j-1])
temp_bin.push_back(bins[i][j]);
}
bins[i] = temp_bin;
}
// find the data range
pair<type1,type1> range1;
pair<type2,type2> range2;
typename graph_traits<Graph>::vertex_iterator vi,vi_end;
range1.first = boost::numeric::bounds<type1>::highest();
range1.second = boost::numeric::bounds<type1>::lowest();
range2.first = boost::numeric::bounds<type2>::highest();
range2.second = boost::numeric::bounds<type2>::lowest();
for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
{
type1 v1 = deg1(*vi, g);
type2 v2 = deg2(*vi, g);
range1.first = min(range1.first, v1);
range1.second = max(range1.second, v1);
range2.first = min(range2.first, v2);
range2.second = max(range2.second, v2);
}
boost::array<pair<val_type, val_type>, 2> data_range;
data_range[0] = range1;
data_range[1] = range2;
hist_t hist(bins, data_range);
SharedHistogram<hist_t> s_hist(hist);
int i, N = num_vertices(g); int i, N = num_vertices(g);
#pragma omp parallel for default(shared) private(i) \ #pragma omp parallel for default(shared) private(i) \
...@@ -51,20 +197,20 @@ struct get_correlation_histogram ...@@ -51,20 +197,20 @@ struct get_correlation_histogram
typename graph_traits<Graph>::vertex_descriptor v = vertex(i, g); typename graph_traits<Graph>::vertex_descriptor v = vertex(i, g);
if (v == graph_traits<Graph>::null_vertex()) if (v == graph_traits<Graph>::null_vertex())
continue; continue;
put_point(v, deg1, deg2, g, weight, s_hist);
typename Hist::key_type key;
key.first = deg1(v, g);
typename graph_traits<Graph>::out_edge_iterator e, e_end;
for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e)
{
key.second = deg2(target(*e,g),g);
s_hist[key] +=
typename Hist::value_type::second_type(get(weight, *e));
}
} }
s_hist.Gather(); s_hist.Gather();
bins = hist.GetBins();
python::list ret_bins;
ret_bins.append(wrap_vector_owned(bins[0]));
ret_bins.append(wrap_vector_owned(bins[1]));
_ret_bins = ret_bins;
_hist = wrap_multi_array_owned<count_type,2>(hist.GetArray());
} }
Hist& _hist; python::object& _hist;
const array<vector<long double>,2>& _bins;
python::object& _ret_bins;
}; };
......