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

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
src/Makefile
src/graph/Makefile
src/graph/correlations/Makefile
src/graph/generation/Makefile
src/graph_tool/Makefile
])
## Process this file with automake to produce Makefile.in
#SUBDIRS = correlations
SUBDIRS = generation correlations
AM_CPPFLAGS =\
-I. -I.. \
......
......@@ -2,6 +2,7 @@
AM_CPPFLAGS =\
-I. -I.. \
-I $(pythondir)/numpy/core/include/numpy/ \
-I../boost-workaround \
-DHAVE_CONFIG_H
......@@ -12,7 +13,7 @@ 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
......@@ -21,33 +22,13 @@ libgraph_tool_correlations_la_includedir = $(pythondir)/graph_tool/include
libgraph_tool_correlations_la_SOURCES = \
graph_assortativity.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_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_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_correlations_combined.cc \
graph_correlations_bind.cc
libgraph_tool_correlations_la_include_HEADERS = \
graph_assortativity.hh \
graph_correlations_combined.hh \
graph_correlations.hh \
graph_correlations_neighbours.hh \
graph_edge_correlations.hh
graph_correlations.hh
libgraph_tool_correlations_la_LIBADD = \
$(PYTHON_LDFLAGS) \
......
......@@ -15,7 +15,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_filtering.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
......@@ -24,48 +23,39 @@
#include "graph_assortativity.hh"
using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool;
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;
try
{
run_action<>()(*this,
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()));
}
run_action<>()(gi,bind<void>(get_assortativity_coefficient(), _1, _2,
var(a), var(a_err)), all_selectors())
(degree_selector(deg, gi));
return make_pair(a, a_err);
}
pair<double,double>
GraphInterface::GetScalarAssortativityCoefficient(GraphInterface::deg_t deg)
const
scalar_assortativity_coefficient(const GraphInterface& gi,
GraphInterface::deg_t deg)
{
using namespace boost::lambda;
double a, a_err;
try
{
run_action<>()(*this, bind<void>(get_scalar_assortativity_coefficient(),
_1, _2, var(a), var(a_err)),
run_action<>()(gi, bind<void>(get_scalar_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()));
}
(degree_selector(deg, gi));
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 @@
#include <tr1/unordered_set>
#include "shared_map.hh"
#include "histogram.hh"
namespace graph_tool
{
......@@ -144,9 +145,8 @@ struct get_scalar_assortativity_coefficient
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);
double avg_a = GetMapMean(a), avg_b = GetMapMean(b);
double da = GetMapDeviation(a,avg_a), db = GetMapDeviation(b,avg_b);
if (da*db > 0)
r = (t1 - avg_a*avg_b)/(da*db);
......
......@@ -18,6 +18,7 @@
#include "graph_filtering.hh"
#include <boost/lambda/bind.hpp>
#include <boost/python.hpp>
#include "graph.hh"
#include "histogram.hh"
......@@ -26,6 +27,8 @@
#include "graph_correlations.hh"
#include <iostream>
using namespace std;
using namespace boost;
using namespace boost::lambda;
......@@ -33,51 +36,63 @@ using namespace graph_tool;
// implementations spread across different compile units to minimize memory
// usage during compilation
void graph_correlations_imp1(const GraphInterface& g, hist2d_t& hist,
boost::any deg1, boost::any deg2,
boost::any weight);
void graph_correlations_imp2(const GraphInterface& g, hist2d_t& hist,
void graph_correlations_imp1(const GraphInterface& g, python::object& hist,
python::object& ret_bins,
boost::any deg1, boost::any deg2,
boost::any weight);
void graph_correlations_imp3(const GraphInterface& g, hist2d_t& hist,
boost::any deg1, boost::any deg2,
boost::any weight);
boost::any weight,
const array<vector<long double>,2>& bins);
typedef ConstantPropertyMap<int,GraphInterface::edge_t> cweight_map_t;
hist2d_t
GraphInterface::GetVertexCorrelationHistogram(GraphInterface::deg_t deg1,
GraphInterface::deg_t deg2,
string weight) const
python::object
get_vertex_correlation_histogram(const GraphInterface& gi,
GraphInterface::deg_t deg1,
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;
if (weight != "")
weight_prop = prop(weight, _edge_index, _properties);
else
weight_prop = cweight_map_t(1);
dynamic_property_map* map =
any_cast<dynamic_property_map*>(edge_prop(weight, gi, true));
weight_prop = wrapped_weight_t(*map);
}
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(),
mpl::vector<cweight_map_t>())
(degree_selector(deg1, _properties),
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);
(degree_selector(deg1, gi), degree_selector(deg2, gi), weight_prop);
}
catch (dynamic_get_failure &e)
catch (ActionNotFound&)
{
throw GraphException("error getting scalar property: " +
string(e.what()));
graph_correlations_imp1(gi, hist, ret_bins, degree_selector(deg1, gi),
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 @@
#define GRAPH_CORRELATIONS_HH
#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"
namespace graph_tool
......@@ -28,12 +33,85 @@ using namespace std;
using namespace boost;
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
{
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,
class WeightMap>
......@@ -41,7 +119,75 @@ struct get_correlation_histogram
WeightMap weight) const
{
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);
#pragma omp parallel for default(shared) private(i) \
......@@ -51,20 +197,20 @@ struct get_correlation_histogram
typename graph_traits<Graph>::vertex_descriptor v = vertex(i, g);
if (v == graph_traits<Graph>::null_vertex())
continue;
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));
}
put_point(v, deg1, deg2, g, weight, s_hist);
}
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;
};
......
......@@ -15,30 +15,17 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_filtering.hh"
#include "graph.hh"
#include "histogram.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
#include "shared_map.hh"
#include <boost/python.hpp>
#include <boost/lambda/bind.hpp>
#include "graph_correlations_combined.hh"
using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool;
void graph_correlations_combined_corr_imp1
(const GraphInterface& g, avg_corr_t& avg_corr,
boost::any deg1, boost::any deg2)
void export_assortativity();
void export_vertex_correlations();
void export_combined_vertex_correlations();
BOOST_PYTHON_MODULE(libgraph_tool_correlations)
{
run_action<>()(g, bind<void>(get_average_combined_degree_correlation(),
_1, _2, _3, var(avg_corr)),
all_selectors(),
graph_tool::detail::split::apply<all_selectors>::type
::second())
(deg1, deg2);
export_assortativity();
export_vertex_correlations();
export_combined_vertex_correlations();
}
......@@ -16,45 +16,52 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_filtering.hh"
#include <boost/lambda/bind.hpp>
#include <boost/python.hpp>
#include "graph.hh"
#include "histogram.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
#include "shared_map.hh"
#include <boost/lambda/bind.hpp>
#include "graph_correlations_combined.hh"
#include "graph_correlations.hh"
using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool;
void graph_correlations_combined_imp1(const GraphInterface& g,
hist2d_t& hist,
boost::any deg1, boost::any deg2);
typedef ConstantPropertyMap<int,GraphInterface::edge_t> dummy_weight;
python::object
get_vertex_combined_correlation_histogram(const GraphInterface& gi,
GraphInterface::deg_t deg1,
GraphInterface::deg_t deg2,
const vector<long double>& xbin,
const vector<long double>& ybin)
{
python::object hist;
python::object ret_bins;
array<vector<long double>,2> bins;
bins[0] = xbin;
bins[1] = ybin;
run_action<>()(gi, lambda::bind<void>
(get_correlation_histogram<GetCombinedPair>(hist, bins,
ret_bins),
lambda::_1, lambda::_2, lambda::_3, dummy_weight(0)),
all_selectors(), all_selectors())
(degree_selector(deg1, gi), degree_selector(deg2, gi));
return python::make_tuple(hist, ret_bins);
}
using namespace boost::python;
hist2d_t
GraphInterface::GetCombinedVertexHistogram(deg_t deg1, deg_t deg2) const
void export_combined_vertex_correlations()
{
hist2d_t hist;
try
{
run_action<>()(*this, bind<void>(get_combined_degree_histogram(),
_1, _2, _3, var(hist)),
all_selectors(),
detail::split::apply<all_selectors>::type::first())
(degree_selector(deg1, _properties),
degree_selector(deg2, _properties));
graph_correlations_combined_imp1(*this, hist,
degree_selector(deg1, _properties),
degree_selector(deg2, _properties));
}
catch (dynamic_get_failure &e)
{
throw GraphException("error getting scalar property: " +