PaGMO  1.1.5
watts_strogatz.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 <algorithm>
26 #include <boost/integer_traits.hpp>
27 #include <boost/random/uniform_int.hpp>
28 #include <boost/numeric/conversion/cast.hpp>
29 #include <cstddef>
30 #include <exception>
31 #include <string>
32 #include <utility>
33 
34 #include "../exceptions.h"
35 #include "../rng.h"
36 #include "base.h"
37 #include "watts_strogatz.h"
38 
39 namespace pagmo { namespace topology {
40 
42 
50 watts_strogatz::watts_strogatz(int k, const double &beta, int n):
51  m_k(boost::numeric_cast<std::size_t>(k)),m_beta(beta),m_drng(rng_generator::get<rng_double>()),m_urng(rng_generator::get<rng_uint32>())
52 {
53  if (m_k < 2 || m_k % 2) {
54  pagmo_throw(value_error,"the K parameter must be even and at least 2");
55  }
56  const vertices_size_type size = boost::numeric_cast<vertices_size_type>(n);
57  // Solve potential overflow.
58  if (m_k == boost::integer_traits<std::size_t>::const_max) {
59  pagmo_throw(std::overflow_error,"overflow error in Watts-Strogatz model");
60  }
61  if (m_beta < 0 || m_beta > 1) {
62  pagmo_throw(value_error,"the beta parameter must be in the [0,1] range");
63  }
64  if (size) {
65  // Add all vertices.
66  for (vertices_size_type i = 0; i < size; ++i) {
67  add_vertex();
68  }
69  // Do the wiring.
70  rewire();
71  }
72 }
73 
75 {
76  return base_ptr(new watts_strogatz(*this));
77 }
78 
79 // Connect an unconnected topology into a Watts-Strogatz model.
80 void watts_strogatz::rewire()
81 {
83  pagmo_assert(size > 0);
84  // First, build the ring lattice.
85  for (std::pair<v_iterator,v_iterator> vertices = get_vertices(); vertices.first != vertices.second; ++vertices.first) {
86  // Forward connections.
87  v_iterator tmp = vertices.first;
88  for (std::size_t j = 1; j <= m_k / 2; ++j) {
89  ++tmp;
90  // Wrap around if we went past the last element.
91  if (tmp == vertices.second) {
92  tmp = get_vertices().first;
93  }
94  add_edge(*vertices.first,*tmp);
95  }
96  // Backward connections.
97  tmp = vertices.first;
98  for (std::size_t j = 1; j <= m_k / 2; ++j) {
99  // Go to the last-past-one element if we are at the first.
100  if (tmp == get_vertices().first) {
101  tmp = vertices.second;
102  }
103  --tmp;
104  add_edge(*vertices.first,*tmp);
105  }
106  }
107  // Second, do the random rewires.
108  boost::uniform_int<vertices_size_type> uint(0,size - 1);
109  for (std::pair<v_iterator,v_iterator> vertices = get_vertices(); vertices.first != vertices.second; ++vertices.first) {
110  v_iterator tmp = vertices.first;
111  ++tmp;
112  const edges_size_type n_adj_vertices = get_num_adjacent_vertices(*vertices.first);
113  // NOTE: here things are hairy, it is possible - especially near the kernel size - that
114  // no rewire can take place because the node is fully connected. We must detect this, in order
115  // to avoid an endless loop. That's why in the for loop we check also the number of vertices adjacent to
116  // vertices.first.
117  // Iterate over the next m_k / 2 vertices, without going past the end.
118  for (std::size_t j = 1; tmp != vertices.second && j <= m_k / 2 && n_adj_vertices < size - 1; ++j, ++tmp) {
119  if (m_drng() < m_beta) {
120  // Select a random vertex that is not vertices.first itself and that would
121  // not end in a duplicate edge if connected from vertices.first.
122  vertices_size_type rng;
123  do {
124  rng = uint(m_urng);
125  } while (rng == *vertices.first || are_adjacent(*vertices.first,rng));
126  pagmo_assert(!are_adjacent(rng,*vertices.first));
127  // Destroy edge and rewire.
128  remove_edge(*vertices.first,*tmp);
129  remove_edge(*tmp,*vertices.first);
130  add_edge(*vertices.first,rng);
131  add_edge(rng,*vertices.first);
132  }
133  }
134  }
135 }
136 
138 {
140  if (size <= m_k + 1) {
141  // If we are below the initial kernel size, just do a fully connected topology.
142  for (std::pair<v_iterator,v_iterator> vertices = get_vertices(); vertices.first != vertices.second; ++vertices.first) {
143  // Do not connect with self.
144  if (n != *vertices.first) {
145  add_edge(n,*vertices.first);
146  add_edge(*vertices.first,n);
147  }
148  }
149  } else {
150  pagmo_assert(size > 0);
151  // We need to do a complete rewire of the model.
153  rewire();
154  }
155 }
156 
157 std::string watts_strogatz::get_name() const
158 {
159  return "Watts-Strogatz";
160 }
161 
162 }} //namespaces
163 
164 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::topology::watts_strogatz)
Root PaGMO namespace.
void add_vertex()
Add a vertex.
Generic thread-safe generator of pseudo-random number generators.
Definition: rng.h:138
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base topology.
Definition: topology/base.h:47
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.
void remove_edge(const vertices_size_type &, const vertices_size_type &)
Remove an edge.
boost::graph_traits< graph_type >::vertex_iterator v_iterator
Iterator over the vertices.
base_ptr clone() const
Clone method.
vertices_size_type get_number_of_vertices() const
Get number of vertices.
watts_strogatz(int=10, const double &=0.05, int=0)
Constructor from K, beta and size.
graph_type::vertices_size_type vertices_size_type
Vertices size type.
edges_size_type get_num_adjacent_vertices(const vertices_size_type &) const
Return the number of adjacent vertices.
std::string get_name() const
Get name of the topology.
Watts-Strogatz network model.
void connect(const vertices_size_type &)
Establish connections between islands during a push_back() operation.
void remove_all_edges()
Remove all edges.
void add_edge(const vertices_size_type &, const vertices_size_type &)
Add an edge.
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.