1
2 package edu.uci.ics.jung.algorithms.generators.random;
3
4 /*
5 * Copyright (c) 2009, The JUNG Authors
6 *
7 * All rights reserved.
8 *
9 * This software is open-source under the BSD license; see either
10 * "license.txt" or
11 * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
12 */
13
14 import java.util.HashMap;
15 import java.util.Map;
16 import java.util.Random;
17
18 import com.google.common.base.Supplier;
19
20 import edu.uci.ics.jung.algorithms.generators.Lattice2DGenerator;
21 import edu.uci.ics.jung.algorithms.util.WeightedChoice;
22 import edu.uci.ics.jung.graph.Graph;
23
24 /**
25 * Graph generator that produces a random graph with small world properties.
26 * The underlying model is an mxn (optionally toroidal) lattice. Each node u
27 * has four local connections, one to each of its neighbors, and
28 * in addition 1+ long range connections to some node v where v is chosen randomly according to
29 * probability proportional to d^-alpha where d is the lattice distance between u and v and alpha
30 * is the clustering exponent.
31 *
32 * @see "Navigation in a small world J. Kleinberg, Nature 406(2000), 845."
33 * @author Joshua O'Madadhain
34 */
35 public class KleinbergSmallWorldGenerator<V, E> extends Lattice2DGenerator<V, E> {
36 private double clustering_exponent;
37 private Random random;
38 private int num_connections = 1;
39
40 /**
41 * Creates an instance with the specified parameters, whose underlying lattice is (a) of size
42 * {@code latticeSize} x {@code latticeSize}, and (b) toroidal.
43 * @param graphFactory factory for graphs of the appropriate type
44 * @param vertexFactory factory for vertices of the appropriate type
45 * @param edgeFactory factory for edges of the appropriate type
46 * @param latticeSize the number of rows and columns of the underlying lattice
47 * @param clusteringExponent the clustering exponent
48 */
49 public KleinbergSmallWorldGenerator(Supplier<? extends Graph<V,E>> graphFactory,
50 Supplier<V> vertexFactory, Supplier<E> edgeFactory,
51 int latticeSize, double clusteringExponent)
52 {
53 this(graphFactory, vertexFactory, edgeFactory, latticeSize, latticeSize, clusteringExponent);
54 }
55
56 /**
57 * Creates an instance with the specified parameters, whose underlying lattice is toroidal.
58 * @param graphFactory factory for graphs of the appropriate type
59 * @param vertexFactory factory for vertices of the appropriate type
60 * @param edgeFactory factory for edges of the appropriate type
61 * @param row_count number of rows of the underlying lattice
62 * @param col_count number of columns of the underlying lattice
63 * @param clusteringExponent the clustering exponent
64 */
65 public KleinbergSmallWorldGenerator(Supplier<? extends Graph<V,E>> graphFactory,
66 Supplier<V> vertexFactory, Supplier<E> edgeFactory,
67 int row_count, int col_count, double clusteringExponent)
68 {
69 super(graphFactory, vertexFactory, edgeFactory, row_count, col_count, true);
70 clustering_exponent = clusteringExponent;
71 initialize();
72 }
73
74 /**
75 * Creates an instance with the specified parameters.
76 * @param graphFactory factory for graphs of the appropriate type
77 * @param vertexFactory factory for vertices of the appropriate type
78 * @param edgeFactory factory for edges of the appropriate type
79 * @param row_count number of rows of the underlying lattice
80 * @param col_count number of columns of the underlying lattice
81 * @param clusteringExponent the clustering exponent
82 * @param isToroidal whether the underlying lattice is toroidal
83 */
84 public KleinbergSmallWorldGenerator(Supplier<? extends Graph<V,E>> graphFactory,
85 Supplier<V> vertexFactory, Supplier<E> edgeFactory,
86 int row_count, int col_count, double clusteringExponent, boolean isToroidal)
87 {
88 super(graphFactory, vertexFactory, edgeFactory, row_count, col_count, isToroidal);
89 clustering_exponent = clusteringExponent;
90 initialize();
91 }
92
93 private void initialize()
94 {
95 this.random = new Random();
96 }
97
98 /**
99 * Sets the {@code Random} instance used by this instance. Useful for
100 * unit testing.
101 * @param random the {@code Random} instance for this class to use
102 */
103 public void setRandom(Random random)
104 {
105 this.random = random;
106 }
107
108 /**
109 * Sets the seed of the internal random number generator. May be used to provide repeatable
110 * experiments.
111 * @param seed the random seed that this class's random number generator is to use
112 */
113 public void setRandomSeed(long seed)
114 {
115 random.setSeed(seed);
116 }
117
118 /**
119 * Sets the number of new 'small-world' connections (outgoing edges) to be added to each vertex.
120 * @param num_connections the number of outgoing small-world edges to add to each vertex
121 */
122 public void setConnectionCount(int num_connections)
123 {
124 if (num_connections <= 0)
125 {
126 throw new IllegalArgumentException("Number of new connections per vertex must be >= 1");
127 }
128 this.num_connections = num_connections;
129 }
130
131 /**
132 * @return the number of new 'small-world' connections that will originate at each vertex
133 */
134 public int getConnectionCount()
135 {
136 return this.num_connections;
137 }
138
139 /**
140 * Generates a random small world network according to the parameters given
141 * @return a random small world graph
142 */
143 @Override
144 public Graph<V,E> get()
145 {
146 Graph<V, E> graph = super.get();
147
148 // TODO: For toroidal graphs, we can make this more clever by pre-creating the WeightedChoice object
149 // and using the output as an offset to the current vertex location.
150 WeightedChoice<V> weighted_choice;
151
152 // Add long range connections
153 for (int i = 0; i < graph.getVertexCount(); i++)
154 {
155 V source = getVertex(i);
156 int row = getRow(i);
157 int col = getCol(i);
158 int row_offset = row < row_count/2 ? -row_count : row_count;
159 int col_offset = col < col_count/2 ? -col_count : col_count;
160
161 Map<V, Float> vertex_weights = new HashMap<V, Float>();
162 for (int j = 0; j < row_count; j++)
163 {
164 for (int k = 0; k < col_count; k++)
165 {
166 if (j == row && k == col)
167 continue;
168 int v_dist = Math.abs(j - row);
169 int h_dist = Math.abs(k - col);
170 if (is_toroidal)
171 {
172 v_dist = Math.min(v_dist, Math.abs(j - row+row_offset));
173 h_dist = Math.min(h_dist, Math.abs(k - col+col_offset));
174 }
175 int distance = v_dist + h_dist;
176 if (distance < 2)
177 continue;
178 else
179 vertex_weights.put(getVertex(j,k), (float)Math.pow(distance, -clustering_exponent));
180 }
181 }
182
183 for (int j = 0; j < this.num_connections; j++) {
184 weighted_choice = new WeightedChoice<V>(vertex_weights, random);
185 V target = weighted_choice.nextItem();
186 graph.addEdge(edge_factory.get(), source, target);
187 }
188 }
189
190 return graph;
191 }
192 }