// Copyright 2004, 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: Nick Edmonds | |
// Andrew Lumsdaine | |
#ifndef BOOST_GRAPH_SSCA_GENERATOR_HPP | |
#define BOOST_GRAPH_SSCA_GENERATOR_HPP | |
#include <iterator> | |
#include <utility> | |
#include <vector> | |
#include <queue> | |
#include <boost/config.hpp> | |
#include <boost/random/uniform_int.hpp> | |
#include <boost/graph/graph_traits.hpp> | |
#include <boost/type_traits/is_base_and_derived.hpp> | |
#include <boost/type_traits/is_same.hpp> | |
enum Direction {FORWARD = 1, BACKWARD = 2, BOTH = FORWARD | BACKWARD}; | |
namespace boost { | |
// This generator generates graphs according to the method specified | |
// in SSCA 1.1. Current versions of SSCA use R-MAT graphs | |
template<typename RandomGenerator, typename Graph> | |
class ssca_iterator | |
{ | |
typedef typename graph_traits<Graph>::directed_category directed_category; | |
typedef typename graph_traits<Graph>::vertices_size_type | |
vertices_size_type; | |
public: | |
typedef std::input_iterator_tag iterator_category; | |
typedef std::pair<vertices_size_type, vertices_size_type> value_type; | |
typedef const value_type& reference; | |
typedef const value_type* pointer; | |
typedef void difference_type; | |
// No argument constructor, set to terminating condition | |
ssca_iterator() | |
: gen(), verticesRemaining(0) { } | |
// Initialize for edge generation | |
ssca_iterator(RandomGenerator& gen, vertices_size_type totVertices, | |
vertices_size_type maxCliqueSize, double probUnidirectional, | |
int maxParallelEdges, double probIntercliqueEdges) | |
: gen(&gen), totVertices(totVertices), maxCliqueSize(maxCliqueSize), | |
probUnidirectional(probUnidirectional), maxParallelEdges(maxParallelEdges), | |
probIntercliqueEdges(probIntercliqueEdges), currentClique(0), | |
verticesRemaining(totVertices) | |
{ | |
cliqueNum = std::vector<int>(totVertices, -1); | |
current = std::make_pair(0,0); | |
} | |
reference operator*() const { return current; } | |
pointer operator->() const { return ¤t; } | |
ssca_iterator& operator++() | |
{ | |
BOOST_USING_STD_MIN(); | |
while (values.empty() && verticesRemaining > 0) { // If there are no values left, generate a new clique | |
uniform_int<vertices_size_type> clique_size(1, maxCliqueSize); | |
uniform_int<vertices_size_type> rand_vertex(0, totVertices-1); | |
uniform_int<int> num_parallel_edges(1, maxParallelEdges); | |
uniform_int<short> direction(0,1); | |
uniform_01<RandomGenerator> prob(*gen); | |
std::vector<vertices_size_type> cliqueVertices; | |
cliqueVertices.clear(); | |
vertices_size_type size = min BOOST_PREVENT_MACRO_SUBSTITUTION (clique_size(*gen), verticesRemaining); | |
while (cliqueVertices.size() < size) { | |
vertices_size_type v = rand_vertex(*gen); | |
if (cliqueNum[v] == -1) { | |
cliqueNum[v] = currentClique; | |
cliqueVertices.push_back(v); | |
verticesRemaining--; | |
} | |
} // Nick: This is inefficient when only a few vertices remain... | |
// I should probably just select the remaining vertices | |
// in order when only a certain fraction remain. | |
typename std::vector<vertices_size_type>::iterator first, second; | |
for (first = cliqueVertices.begin(); first != cliqueVertices.end(); ++first) | |
for (second = first+1; second != cliqueVertices.end(); ++second) { | |
Direction d; | |
int edges; | |
d = prob() < probUnidirectional ? (direction(*gen) == 0 ? FORWARD : BACKWARD) : BOTH; | |
if (d & FORWARD) { | |
edges = num_parallel_edges(*gen); | |
for (int i = 0; i < edges; ++i) | |
values.push(std::make_pair(*first, *second)); | |
} | |
if (d & BACKWARD) { | |
edges = num_parallel_edges(*gen); | |
for (int i = 0; i < edges; ++i) | |
values.push(std::make_pair(*second, *first)); | |
} | |
} | |
if (verticesRemaining == 0) { | |
// Generate interclique edges | |
for (vertices_size_type i = 0; i < totVertices; ++i) { | |
double p = probIntercliqueEdges; | |
for (vertices_size_type d = 2; d < totVertices/2; d *= 2, p/= 2) { | |
vertices_size_type j = (i+d) % totVertices; | |
if (cliqueNum[j] != cliqueNum[i] && prob() < p) { | |
int edges = num_parallel_edges(*gen); | |
for (int i = 0; i < edges; ++i) | |
values.push(std::make_pair(i, j)); | |
} | |
} | |
} | |
} | |
currentClique++; | |
} | |
if (!values.empty()) { // If we're not done return a value | |
current = values.front(); | |
values.pop(); | |
} | |
return *this; | |
} | |
ssca_iterator operator++(int) | |
{ | |
ssca_iterator temp(*this); | |
++(*this); | |
return temp; | |
} | |
bool operator==(const ssca_iterator& other) const | |
{ | |
return verticesRemaining == other.verticesRemaining && values.empty() && other.values.empty(); | |
} | |
bool operator!=(const ssca_iterator& other) const | |
{ return !(*this == other); } | |
private: | |
// Parameters | |
RandomGenerator* gen; | |
vertices_size_type totVertices; | |
vertices_size_type maxCliqueSize; | |
double probUnidirectional; | |
int maxParallelEdges; | |
double probIntercliqueEdges; | |
// Internal data structures | |
std::vector<int> cliqueNum; | |
std::queue<value_type> values; | |
int currentClique; | |
vertices_size_type verticesRemaining; | |
value_type current; | |
}; | |
} // end namespace boost | |
#endif // BOOST_GRAPH_SSCA_GENERATOR_HPP |