PaGMO  1.1.5
barabasi_albert.cpp
1 /*****************************************************************************
2  * Copyright (C) 2004-2015 The PaGMO development team, *
3  * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4  * *
5  * https://github.com/esa/pagmo *
6  * *
7  * act@esa.int *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  *****************************************************************************/
24 
25 #include <boost/numeric/conversion/cast.hpp>
26 #include <boost/random/uniform_int.hpp>
27 #include <cstddef>
28 #include <iterator>
29 #include <sstream>
30 #include <string>
31 #include <utility>
32 
33 
34 #include "../exceptions.h"
35 #include "../rng.h"
36 #include "barabasi_albert.h"
37 #include "base.h"
38 
39 namespace pagmo { namespace topology {
40 
42 
50  m_m0(boost::numeric_cast<std::size_t>(m0)),m_m(boost::numeric_cast<std::size_t>(m)),
51  m_drng(rng_generator::get<rng_double>()),m_urng(rng_generator::get<rng_uint32>())
52 {
53  if (m0 < 2 || m < 1 || m > m0) {
54  pagmo_throw(value_error,"the value of m and m0 must be at least 1 and 2, and m must not be greater than m0");
55  }
56 }
57 
59 {
60  return base_ptr(new barabasi_albert(*this));
61 }
62 
64 {
65  pagmo_assert(get_number_of_vertices() > 0);
66  const vertices_size_type prev_size = get_number_of_vertices() - 1;
67  if (prev_size < m_m0) {
68  // If we had not built the initial m0 nodes, do it.
69  // We want to connect the newcomer island with high probability, and make sure that
70  // at least one connection exists (otherwise the island stays isolated).
71  // NOTE: is it worth to make it a user-tunable parameter?
72  const double prob = 0.8;
73  // Flag indicating if at least 1 connection was added.
74  bool connection_added = false;
75  // Main loop.
76  for (std::pair<v_iterator,v_iterator> vertices = get_vertices(); vertices.first != vertices.second; ++vertices.first) {
77  // Do not consider the new vertex itself.
78  if (*vertices.first != idx) {
79  if (m_drng() < prob) {
80  connection_added = true;
81  // Add the connections
82  add_edge(*vertices.first,idx);
83  add_edge(idx,*vertices.first);
84  }
85  }
86  }
87  // If no connections were established and this is not the first island being inserted,
88  // establish at least one connection with a random island other than n.
89  if ((!connection_added) && (prev_size != 0)) {
90  // Get a random vertex index between 0 and n_vertices - 1. Keep on repeating the procedure if by
91  // chance we end up on idx again.
92  boost::uniform_int<vertices_size_type> uni_int(0,get_number_of_vertices() - 1);
94  do {
95  rnd = uni_int(m_urng);
96  } while (rnd == idx);
97  // Add connections to the random vertex.
98  add_edge(rnd,idx);
99  add_edge(idx,rnd);
100  }
101  } else {
102  // Now we need to add m edges, choosing the nodes with a probability
103  // proportional to their number of connections. We keep track of the
104  // connection established in order to avoid connecting twice to the same
105  // node.
106  std::size_t i = 0;
107  std::pair<v_iterator,v_iterator> vertices;
108  std::pair<a_iterator,a_iterator> adj_vertices;
109  while (i < m_m) {
110  // Let's find the current total number of edges.
111  const edges_size_type n_edges = get_number_of_edges();
112  pagmo_assert(n_edges > 0);
113  boost::uniform_int<edges_size_type> uni_int(0,n_edges - 1 - i);
114  // Here we choose a random number between 0 and n_edges - 1 - i.
115  const edges_size_type rn = uni_int(m_urng);
116  edges_size_type n = 0;
117  // Iterate over all vertices and accumulate the number of edges for each of them. Stop when the accumulated number of edges is greater
118  // than rn. This is equivalent to giving a chance of connection to vertex v directly proportional to the number of edges departing from v.
119  // You can think of this process as selecting a random edge among all the existing edges and connecting to the vertex from which the
120  // selected edge departs.
121  vertices = get_vertices();
122  for (; vertices.first != vertices.second; ++vertices.first) {
123  // Do not consider it_n.
124  if (*vertices.first != idx) {
125  adj_vertices = get_adjacent_vertices(*vertices.first);
126  n += boost::numeric_cast<edges_size_type>(std::distance(adj_vertices.first,adj_vertices.second));
127  //std::cout << "vertex " << *vertices.first << "rn is " << rn << " and n is " << n << std::endl;
128  if (n > rn) {
129  break;
130  }
131  }
132  }
133  pagmo_assert(vertices.first != vertices.second);
134  // If the candidate was not already connected, then add it.
135  if (!are_adjacent(idx,*vertices.first)) {
136  add_edge(*vertices.first,idx);
137  add_edge(idx,*vertices.first);
138  ++i;
139  }
140  }
141  }
142 }
143 
145 
151 {
152  std::ostringstream oss;
153  oss << "\tm0 = " << m_m0 << '\n';
154  oss << "\tm = " << m_m << '\n';
155  return oss.str();
156 }
157 
158 std::string barabasi_albert::get_name() const
159 {
160  return "Barabasi-Albert";
161 }
162 
163 }} //namespaces
164 
165 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::topology::barabasi_albert)
Root PaGMO namespace.
Generic thread-safe generator of pseudo-random number generators.
Definition: rng.h:138
base_ptr clone() const
Clone method.
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base topology.
Definition: topology/base.h:47
void connect(const vertices_size_type &)
Establish connections between islands during a push_back() operation.
This rng returns an unsigned integer in the [0,2**32-1] range.
Definition: rng.h:47
graph_type::edges_size_type edges_size_type
Edges size type.
STL namespace.
std::string get_name() const
Get name of the topology.
std::string human_readable_extra() const
Topology-specific human readable info.
vertices_size_type get_number_of_vertices() const
Get number of vertices.
graph_type::vertices_size_type vertices_size_type
Vertices size type.
Barabási-Albert graph topology.
void add_edge(const vertices_size_type &, const vertices_size_type &)
Add an edge.
edges_size_type get_number_of_edges() const
Get number of edges.
std::pair< a_iterator, a_iterator > get_adjacent_vertices(const vertices_size_type &) const
Return iterator range to adjacent vertices.
barabasi_albert(int m0=3, int m=2)
Constructor from kernel size and number of edges.
This rng returns a double in the [0,1[ range.
Definition: rng.h:89
std::pair< v_iterator, v_iterator > get_vertices() const
Return iterator range to vertices.
bool are_adjacent(const vertices_size_type &, const vertices_size_type &) const
Return true if two vertices are adjacent.