2
0
mirror of https://github.com/boostorg/graph.git synced 2026-02-01 08:32:11 +00:00
Files
graph/test/csr_graph_test.cpp
Jeremiah Willcock 801a11bf4a Merged in changes from trunk for Boost.Graph and Boost.PropertyMap. Includes
r56013, r56014, r56015, r56016, r56017, r56089, r56097, r56116, r56117, r56126,
r56127, r56128, r56140, r56147, r56300, r56301, r56339, r56360, r56454, r56473,
r56563, r56651, r56654, r56658, r56682, r56732, r56796, r56855, r56856, r56868,
r55667, r56860, r55473, r55507, r55528, r55749, r56147, r55723, r56109, r56859,
and r55780.


[SVN r56881]
2009-10-15 20:40:46 +00:00

513 lines
19 KiB
C++

// Copyright 2005 The Trustees of Indiana University.
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// Authors: Jeremiah Willcock
// Douglas Gregor
// Andrew Lumsdaine
// The libstdc++ debug mode makes this test run for hours...
#ifdef _GLIBCXX_DEBUG
# undef _GLIBCXX_DEBUG
#endif
// Use new CSR interface
#define BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Test for the compressed sparse row graph type
#include <boost/graph/compressed_sparse_row_graph.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/erdos_renyi_generator.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/random/linear_congruential.hpp>
#include <cassert>
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <boost/lexical_cast.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/limits.hpp>
#include <string>
#include <boost/graph/iteration_macros.hpp>
#include <boost/test/minimal.hpp>
// Algorithms to test against
#include <boost/graph/betweenness_centrality.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
typedef boost::adjacency_list<> GraphT;
typedef boost::erdos_renyi_iterator<boost::minstd_rand, GraphT> ERGen;
struct VertexData
{
int index;
};
typedef boost::compressed_sparse_row_graph<boost::directedS, VertexData>
CSRGraphT;
typedef boost::compressed_sparse_row_graph<boost::bidirectionalS, VertexData>
BidirCSRGraphT;
template <class G1, class VI1, class G2, class VI2, class IsomorphismMap>
void assert_graphs_equal(const G1& g1, const VI1& vi1,
const G2& g2, const VI2& vi2,
const IsomorphismMap& iso) {
using boost::out_degree;
BOOST_CHECK (num_vertices(g1) == num_vertices(g2));
BOOST_CHECK (num_edges(g1) == num_edges(g2));
typedef typename boost::graph_traits<G1>::vertex_iterator vertiter1;
{
vertiter1 i, iend;
for (boost::tie(i, iend) = vertices(g1); i != iend; ++i) {
typename boost::graph_traits<G1>::vertex_descriptor v1 = *i;
typename boost::graph_traits<G2>::vertex_descriptor v2 = iso[v1];
BOOST_CHECK (vi1[v1] == vi2[v2]);
BOOST_CHECK (out_degree(v1, g1) == out_degree(v2, g2));
std::vector<std::size_t> edges1(out_degree(v1, g1));
typename boost::graph_traits<G1>::out_edge_iterator oe1, oe1end;
for (boost::tie(oe1, oe1end) = out_edges(v1, g1); oe1 != oe1end; ++oe1) {
BOOST_CHECK (source(*oe1, g1) == v1);
edges1.push_back(vi1[target(*oe1, g1)]);
}
std::vector<std::size_t> edges2(out_degree(v2, g2));
typename boost::graph_traits<G2>::out_edge_iterator oe2, oe2end;
for (boost::tie(oe2, oe2end) = out_edges(v2, g2); oe2 != oe2end; ++oe2) {
BOOST_CHECK (source(*oe2, g2) == v2);
edges2.push_back(vi2[target(*oe2, g2)]);
}
std::sort(edges1.begin(), edges1.end());
std::sort(edges2.begin(), edges2.end());
if (edges1 != edges2) {
std::cerr << "For vertex " << v1 << std::endl;
std::cerr << "edges1:";
for (size_t i = 0; i < edges1.size(); ++i) std::cerr << " " << edges1[i];
std::cerr << std::endl;
std::cerr << "edges2:";
for (size_t i = 0; i < edges2.size(); ++i) std::cerr << " " << edges2[i];
std::cerr << std::endl;
}
BOOST_CHECK (edges1 == edges2);
}
}
{
std::vector<std::pair<std::size_t, std::size_t> > all_edges1;
std::vector<std::pair<std::size_t, std::size_t> > all_edges2;
typename boost::graph_traits<G1>::edge_iterator ei1, ei1end;
for (boost::tie(ei1, ei1end) = edges(g1); ei1 != ei1end; ++ei1)
all_edges1.push_back(std::make_pair(vi1[source(*ei1, g1)],
vi1[target(*ei1, g1)]));
typename boost::graph_traits<G2>::edge_iterator ei2, ei2end;
for (boost::tie(ei2, ei2end) = edges(g2); ei2 != ei2end; ++ei2)
all_edges2.push_back(std::make_pair(vi2[source(*ei2, g2)],
vi2[target(*ei2, g2)]));
std::sort(all_edges1.begin(), all_edges1.end());
std::sort(all_edges2.begin(), all_edges2.end());
BOOST_CHECK (all_edges1 == all_edges2);
}
}
template <typename Structure>
void check_consistency_one(const Structure& g) {
// Do a bunch of tests on the graph internal data
#ifndef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Check that m_last_source is valid
BOOST_CHECK(g.m_last_source <= g.m_rowstart.size() - 1);
#endif // !BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Check that m_rowstart entries are valid, and that entries after
// m_last_source + 1 are all zero
BOOST_CHECK(g.m_rowstart[0] == 0);
for (size_t i = 0;
#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
i < g.m_rowstart.size() - 1;
#else // !BOOST_GRAPH_USE_NEW_CSR_INTERFACE
i < g.m_last_source;
#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
++i) {
BOOST_CHECK(g.m_rowstart[i + 1] >= g.m_rowstart[i]);
BOOST_CHECK(g.m_rowstart[i + 1] <= g.m_rowstart.back());
}
#ifndef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
for (size_t i = g.m_last_source + 1;
i < g.m_rowstart.size(); ++i) {
BOOST_CHECK(g.m_forward.m_rowstart[i] == 0);
}
#endif // !BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Check that m_column entries are within range
for (size_t i = 0; i < g.m_rowstart.back(); ++i) {
BOOST_CHECK(g.m_column[i] < g.m_rowstart.size() - 1);
}
}
template <typename Graph>
void check_consistency_dispatch(const Graph& g,
boost::incidence_graph_tag) {
check_consistency_one(g.m_forward);
}
template <class G>
void assert_bidir_equal_in_both_dirs(const G& g) {
BOOST_CHECK (g.m_forward.m_rowstart.size() == g.m_backward.m_rowstart.size());
BOOST_CHECK (g.m_forward.m_column.size() == g.m_backward.m_column.size());
typedef typename boost::graph_traits<G>::vertex_descriptor Vertex;
typedef typename boost::graph_traits<G>::edges_size_type EdgeIndex;
std::vector<boost::tuple<EdgeIndex, Vertex, Vertex> > edges_forward, edges_backward;
for (Vertex i = 0; i < g.m_forward.m_rowstart.size() - 1; ++i) {
for (EdgeIndex j = g.m_forward.m_rowstart[i];
j < g.m_forward.m_rowstart[i + 1]; ++j) {
edges_forward.push_back(boost::make_tuple(j, i, g.m_forward.m_column[j]));
}
}
for (Vertex i = 0; i < g.m_backward.m_rowstart.size() - 1; ++i) {
for (EdgeIndex j = g.m_backward.m_rowstart[i];
j < g.m_backward.m_rowstart[i + 1]; ++j) {
edges_backward.push_back(boost::make_tuple(g.m_backward.m_edge_properties[j], g.m_backward.m_column[j], i));
}
}
std::sort(edges_forward.begin(), edges_forward.end());
std::sort(edges_backward.begin(), edges_backward.end());
BOOST_CHECK (edges_forward == edges_backward);
}
template <typename Graph>
void check_consistency_dispatch(const Graph& g,
boost::bidirectional_graph_tag) {
check_consistency_one(g.m_forward);
check_consistency_one(g.m_backward);
assert_bidir_equal_in_both_dirs(g);
}
template <typename Graph>
void check_consistency(const Graph& g) {
check_consistency_dispatch(g, typename boost::graph_traits<Graph>::traversal_category());
}
template<typename OrigGraph>
void graph_test(const OrigGraph& g)
{
// Check copying of a graph
CSRGraphT g2(g);
check_consistency(g2);
BOOST_CHECK((std::size_t)std::distance(edges(g2).first, edges(g2).second)
== num_edges(g2));
assert_graphs_equal(g, boost::identity_property_map(),
g2, boost::identity_property_map(),
boost::identity_property_map());
// Check constructing a graph from iterators
CSRGraphT g3(boost::edges_are_sorted,
boost::make_transform_iterator(edges(g2).first,
boost::detail::make_edge_to_index_pair(g2)),
boost::make_transform_iterator(edges(g2).second,
boost::detail::make_edge_to_index_pair(g2)),
num_vertices(g));
check_consistency(g3);
BOOST_CHECK((std::size_t)std::distance(edges(g3).first, edges(g3).second)
== num_edges(g3));
assert_graphs_equal(g2, boost::identity_property_map(),
g3, boost::identity_property_map(),
boost::identity_property_map());
#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Check constructing a graph using in-place modification of vectors
{
std::vector<std::size_t> sources(num_edges(g2));
std::vector<std::size_t> targets(num_edges(g2));
std::size_t idx = 0;
// Edges actually sorted
BGL_FORALL_EDGES(e, g2, CSRGraphT) {
sources[idx] = source(e, g2);
targets[idx] = target(e, g2);
++idx;
}
CSRGraphT g3a(boost::construct_inplace_from_sources_and_targets,
sources,
targets,
num_vertices(g2));
check_consistency(g3a);
assert_graphs_equal(g2, boost::identity_property_map(),
g3a, boost::identity_property_map(),
boost::identity_property_map());
}
{
std::vector<std::size_t> sources(num_edges(g2));
std::vector<std::size_t> targets(num_edges(g2));
std::size_t idx = 0;
// Edges reverse-sorted
BGL_FORALL_EDGES(e, g2, CSRGraphT) {
sources[num_edges(g2) - 1 - idx] = source(e, g2);
targets[num_edges(g2) - 1 - idx] = target(e, g2);
++idx;
}
CSRGraphT g3a(boost::construct_inplace_from_sources_and_targets,
sources,
targets,
num_vertices(g2));
check_consistency(g3a);
assert_graphs_equal(g2, boost::identity_property_map(),
g3a, boost::identity_property_map(),
boost::identity_property_map());
}
{
std::vector<std::size_t> sources(num_edges(g2));
std::vector<std::size_t> targets(num_edges(g2));
std::size_t idx = 0;
// Edges scrambled using Fisher-Yates shuffle (Durstenfeld variant) from
// Wikipedia
BGL_FORALL_EDGES(e, g2, CSRGraphT) {
sources[idx] = source(e, g2);
targets[idx] = target(e, g2);
++idx;
}
boost::minstd_rand gen(1);
if (num_edges(g) != 0) {
for (std::size_t i = num_edges(g) - 1; i > 0; --i) {
std::size_t scrambled = boost::uniform_int<>(0, i)(gen);
if (scrambled == i) continue;
using std::swap;
swap(sources[i], sources[scrambled]);
swap(targets[i], targets[scrambled]);
}
}
CSRGraphT g3a(boost::construct_inplace_from_sources_and_targets,
sources,
targets,
num_vertices(g2));
check_consistency(g3a);
assert_graphs_equal(g2, boost::identity_property_map(),
g3a, boost::identity_property_map(),
boost::identity_property_map());
}
#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
CSRGraphT::edge_iterator ei, ei_end;
#ifndef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Check constructing a graph using add_edge and add_vertices
CSRGraphT g4;
BOOST_CHECK(num_vertices(g4) == 0);
std::size_t first_vert = add_vertices(num_vertices(g3), g4);
BGL_FORALL_VERTICES(v, g4, CSRGraphT)
g4[v].index = v;
BOOST_CHECK(first_vert == 0);
BOOST_CHECK(num_vertices(g4) == num_vertices(g3));
int i;
for (boost::tie(ei, ei_end) = edges(g3), i = 0; ei != ei_end; ++ei, ++i) {
CSRGraphT::edge_descriptor e = add_edge(source(*ei, g3), target(*ei, g3), g4);
BOOST_CHECK(source(e, g4) == source(*ei, g3));
BOOST_CHECK(target(e, g4) == target(*ei, g3));
if (i % 13 == 0) check_consistency(g4);
}
assert_graphs_equal(g3, boost::identity_property_map(),
g4, boost::identity_property_map(),
boost::identity_property_map());
#endif // !BOOST_GRAPH_USE_NEW_CSR_INTERFACE
// Check edge_from_index (and implicitly the edge_index property map) for
// each edge in g2
std::size_t last_src = 0, last_tgt = 0;
for (boost::tie(ei, ei_end) = edges(g2); ei != ei_end; ++ei) {
BOOST_CHECK(edge_from_index(get(boost::edge_index, g2, *ei), g2) == *ei);
std::size_t src = get(boost::vertex_index, g2, source(*ei, g2));
std::size_t tgt = get(boost::vertex_index, g2, target(*ei, g2));
BOOST_CHECK(src >= last_src);
last_src = src;
last_tgt = tgt;
}
// Check out edge iteration and vertex iteration for sortedness
CSRGraphT::vertex_iterator vi, vi_end;
std::size_t last_vertex = 0;
bool first_iter = true;
for (boost::tie(vi, vi_end) = vertices(g2); vi != vi_end; ++vi) {
std::size_t v = get(boost::vertex_index, g2, *vi);
BOOST_CHECK(first_iter || v > last_vertex);
last_vertex = v;
first_iter = false;
CSRGraphT::out_edge_iterator oei, oei_end;
for (boost::tie(oei, oei_end) = out_edges(*vi, g2); oei != oei_end; ++oei) {
BOOST_CHECK(source(*oei, g2) == *vi);
}
// Find a vertex for testing
CSRGraphT::vertex_descriptor test_vertex = vertex(num_vertices(g2) / 2, g2);
int edge_count = 0;
CSRGraphT::out_edge_iterator oei2, oei2_end;
for (boost::tie(oei2, oei_end) = out_edges(*vi, g2); oei2 != oei_end; ++oei2) {
if (target(*oei2, g2) == test_vertex)
++edge_count;
}
}
// Run brandes_betweenness_centrality, which touches on a whole lot
// of things, including VertexListGraph and IncidenceGraph
using namespace boost;
std::vector<double> vertex_centralities(num_vertices(g3));
std::vector<double> edge_centralities(num_edges(g3));
brandes_betweenness_centrality
(g3,
make_iterator_property_map(vertex_centralities.begin(),
get(boost::vertex_index, g3)),
make_iterator_property_map(edge_centralities.begin(),
get(boost::edge_index, g3)));
// Extra qualifications for aCC
// Invert the edge centralities and use these as weights to
// Kruskal's MST algorithm, which will test the EdgeListGraph
// capabilities.
double max_val = (std::numeric_limits<double>::max)();
for (std::size_t i = 0; i < num_edges(g3); ++i)
edge_centralities[i] =
edge_centralities[i] == 0.0? max_val : 1.0 / edge_centralities[i];
typedef boost::graph_traits<CSRGraphT>::edge_descriptor edge_descriptor;
std::vector<edge_descriptor> mst_edges;
mst_edges.reserve(num_vertices(g3));
kruskal_minimum_spanning_tree
(g3, std::back_inserter(mst_edges),
weight_map(make_iterator_property_map(edge_centralities.begin(),
get(boost::edge_index, g3))));
}
void graph_test(int nnodes, double density, int seed)
{
boost::minstd_rand gen(seed);
std::cout << "Testing " << nnodes << " density " << density << std::endl;
GraphT g(ERGen(gen, nnodes, density), ERGen(), nnodes);
graph_test(g);
}
void test_graph_properties()
{
using namespace boost;
typedef compressed_sparse_row_graph<directedS,
no_property,
no_property,
property<graph_name_t, std::string> >
CSRGraphT;
CSRGraphT g;
BOOST_CHECK(get_property(g, graph_name) == "");
set_property(g, graph_name, "beep");
BOOST_CHECK(get_property(g, graph_name) == "beep");
}
struct Vertex
{
double centrality;
};
struct Edge
{
Edge(double weight) : weight(weight), centrality(0.0) { }
double weight;
double centrality;
};
void test_vertex_and_edge_properties()
{
using namespace boost;
typedef compressed_sparse_row_graph<directedS, Vertex, Edge>
CSRGraphWithPropsT;
typedef std::pair<int, int> E;
E edges_init[6] = { E(0, 1), E(0, 3), E(1, 2), E(3, 1), E(3, 4), E(4, 2) };
double weights[6] = { 1.0, 1.0, 0.5, 1.0, 1.0, 0.5 };
double centrality[5] = { 0.0, 1.5, 0.0, 1.0, 0.5 };
CSRGraphWithPropsT g(boost::edges_are_sorted, &edges_init[0], &edges_init[0] + 6, &weights[0], 5, 6);
brandes_betweenness_centrality
(g,
centrality_map(get(&Vertex::centrality, g)).
weight_map(get(&Edge::weight, g)).
edge_centrality_map(get(&Edge::centrality, g)));
BGL_FORALL_VERTICES(v, g, CSRGraphWithPropsT)
BOOST_CHECK(g[v].centrality == centrality[v]);
}
int test_main(int argc, char* argv[])
{
// Optionally accept a seed value
int seed = std::time(0);
if (argc > 1) seed = boost::lexical_cast<int>(argv[1]);
std::cout << "Seed = " << seed << std::endl;
{
std::cout << "Testing empty graph" << std::endl;
CSRGraphT g;
graph_test(g);
}
// graph_test(1000, 0.05, seed);
// graph_test(1000, 0.0, seed);
// graph_test(1000, 0.1, seed);
graph_test(1000, 0.001, seed);
graph_test(1000, 0.0005, seed);
#ifndef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
{
std::cout << "Testing partially constructed CSR graph" << std::endl;
CSRGraphT g;
add_vertices(std::size_t(5), g);
add_edge(std::size_t(1), std::size_t(2), g);
check_consistency(g);
add_edge(std::size_t(2), std::size_t(3), g);
check_consistency(g);
add_edge(std::size_t(2), std::size_t(4), g);
check_consistency(g);
CSRGraphT::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
BOOST_CHECK(edge_from_index(get(boost::edge_index, g, *ei), g) == *ei);
}
graph_test(g);
}
#endif // !BOOST_GRAPH_USE_NEW_CSR_INTERFACE
test_graph_properties();
test_vertex_and_edge_properties();
#ifdef BOOST_GRAPH_USE_NEW_CSR_INTERFACE
{
std::cout << "Testing CSR graph built from unsorted edges" << std::endl;
std::pair<int, int> unsorted_edges[] = {std::make_pair(5, 0), std::make_pair(3, 2), std::make_pair(4, 1), std::make_pair(4, 0), std::make_pair(0, 2), std::make_pair(5, 2)};
CSRGraphT g(boost::edges_are_unsorted, unsorted_edges, unsorted_edges + sizeof(unsorted_edges) / sizeof(*unsorted_edges), 6);
CSRGraphT g2(boost::edges_are_unsorted_multi_pass, unsorted_edges, unsorted_edges + sizeof(unsorted_edges) / sizeof(*unsorted_edges), 6);
graph_test(g);
graph_test(g2);
assert_graphs_equal(g, boost::identity_property_map(),
g2, boost::identity_property_map(),
boost::identity_property_map());
std::cout << "Testing bidir CSR graph built from unsorted edges" << std::endl;
BidirCSRGraphT g2b(boost::edges_are_unsorted_multi_pass, unsorted_edges, unsorted_edges + sizeof(unsorted_edges) / sizeof(*unsorted_edges), 6);
graph_test(g2b);
assert_graphs_equal(g, boost::identity_property_map(),
g2b, boost::identity_property_map(),
boost::identity_property_map());
std::cout << "Testing CSR graph built using add_edges" << std::endl;
// Test building a graph using add_edges on unsorted lists
CSRGraphT g3(boost::edges_are_unsorted, unsorted_edges, unsorted_edges, 6); // Empty range
add_edges(unsorted_edges, unsorted_edges + 3, g3);
boost::no_property edge_data[3];
add_edges(unsorted_edges + 3, unsorted_edges + 6, edge_data, edge_data + 3, g3);
graph_test(g3);
assert_graphs_equal(g, boost::identity_property_map(),
g3, boost::identity_property_map(),
boost::identity_property_map());
}
#endif // BOOST_GRAPH_USE_NEW_CSR_INTERFACE
return 0;
}