View Javadoc
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 }