--- /dev/null
+
+package edu.uci.ics.jung.algorithms.generators.random;
+
+/*
+* Copyright (c) 2009, the JUNG Project and the Regents of the University
+* of California
+* All rights reserved.
+*
+* This software is open-source under the BSD license; see either
+* "license.txt" or
+* http://jung.sourceforge.net/license.txt for a description.
+*/
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import org.apache.commons.collections15.Factory;
+
+import edu.uci.ics.jung.algorithms.generators.Lattice2DGenerator;
+import edu.uci.ics.jung.algorithms.util.WeightedChoice;
+import edu.uci.ics.jung.graph.Graph;
+
+/**
+ * Graph generator that produces a random graph with small world properties.
+ * The underlying model is an mxn (optionally toroidal) lattice. Each node u
+ * has four local connections, one to each of its neighbors, and
+ * in addition 1+ long range connections to some node v where v is chosen randomly according to
+ * probability proportional to d^-alpha where d is the lattice distance between u and v and alpha
+ * is the clustering exponent.
+ *
+ * @see "Navigation in a small world J. Kleinberg, Nature 406(2000), 845."
+ * @author Joshua O'Madadhain
+ */
+public class KleinbergSmallWorldGenerator<V, E> extends Lattice2DGenerator<V, E> {
+ private double clustering_exponent;
+ private Random random;
+ private int num_connections = 1;
+
+ /**
+ * Creates
+ * @param graph_factory
+ * @param vertex_factory
+ * @param edge_factory
+ * @param latticeSize
+ * @param clusteringExponent
+ */
+ public KleinbergSmallWorldGenerator(Factory<? extends Graph<V,E>> graph_factory, Factory<V> vertex_factory,
+ Factory<E> edge_factory, int latticeSize, double clusteringExponent)
+ {
+ this(graph_factory, vertex_factory, edge_factory, latticeSize, latticeSize, clusteringExponent);
+ }
+
+ /**
+ * @param graph_factory
+ * @param vertex_factory
+ * @param edge_factory
+ * @param row_count
+ * @param col_count
+ * @param clusteringExponent
+ */
+ public KleinbergSmallWorldGenerator(Factory<? extends Graph<V,E>> graph_factory, Factory<V> vertex_factory,
+ Factory<E> edge_factory, int row_count, int col_count, double clusteringExponent)
+ {
+ super(graph_factory, vertex_factory, edge_factory, row_count, col_count, true);
+ clustering_exponent = clusteringExponent;
+ initialize();
+ }
+
+ /**
+ * @param graph_factory
+ * @param vertex_factory
+ * @param edge_factory
+ * @param row_count
+ * @param col_count
+ * @param clusteringExponent
+ * @param isToroidal
+ */
+ public KleinbergSmallWorldGenerator(Factory<? extends Graph<V,E>> graph_factory, Factory<V> vertex_factory,
+ Factory<E> edge_factory, int row_count, int col_count, double clusteringExponent,
+ boolean isToroidal)
+ {
+ super(graph_factory, vertex_factory, edge_factory, row_count, col_count, isToroidal);
+ clustering_exponent = clusteringExponent;
+ initialize();
+ }
+
+ private void initialize()
+ {
+ this.random = new Random();
+ }
+
+ /**
+ * Sets the {@code Random} instance used by this instance. Useful for
+ * unit testing.
+ */
+ public void setRandom(Random random)
+ {
+ this.random = random;
+ }
+
+ /**
+ * Sets the seed of the internal random number generator. May be used to provide repeatable
+ * experiments.
+ */
+ public void setRandomSeed(long seed)
+ {
+ random.setSeed(seed);
+ }
+
+ /**
+ * Sets the number of new 'small-world' connections (outgoing edges) to be added to each vertex.
+ */
+ public void setConnectionCount(int num_connections)
+ {
+ if (num_connections <= 0)
+ {
+ throw new IllegalArgumentException("Number of new connections per vertex must be >= 1");
+ }
+ this.num_connections = num_connections;
+ }
+
+ /**
+ * Returns the number of new 'small-world' connections to be made to each vertex.
+ */
+ public int getConnectionCount()
+ {
+ return this.num_connections;
+ }
+
+ /**
+ * Generates a random small world network according to the parameters given
+ * @return a random small world graph
+ */
+ @Override
+ public Graph<V,E> create()
+ {
+ Graph<V, E> graph = super.create();
+
+ // TODO: For toroidal graphs, we can make this more clever by pre-creating the WeightedChoice object
+ // and using the output as an offset to the current vertex location.
+ WeightedChoice<V> weighted_choice;
+
+ // Add long range connections
+ for (int i = 0; i < graph.getVertexCount(); i++)
+ {
+ V source = getVertex(i);
+ int row = getRow(i);
+ int col = getCol(i);
+ int row_offset = row < row_count/2 ? -row_count : row_count;
+ int col_offset = col < col_count/2 ? -col_count : col_count;
+
+ Map<V, Float> vertex_weights = new HashMap<V, Float>();
+ for (int j = 0; j < row_count; j++)
+ {
+ for (int k = 0; k < col_count; k++)
+ {
+ if (j == row && k == col)
+ continue;
+ int v_dist = Math.abs(j - row);
+ int h_dist = Math.abs(k - col);
+ if (is_toroidal)
+ {
+ v_dist = Math.min(v_dist, Math.abs(j - row+row_offset));
+ h_dist = Math.min(h_dist, Math.abs(k - col+col_offset));
+ }
+ int distance = v_dist + h_dist;
+ if (distance < 2)
+ continue;
+ else
+ vertex_weights.put(getVertex(j,k), (float)Math.pow(distance, -clustering_exponent));
+ }
+ }
+
+ for (int j = 0; j < this.num_connections; j++) {
+ weighted_choice = new WeightedChoice<V>(vertex_weights, random);
+ V target = weighted_choice.nextItem();
+ graph.addEdge(edge_factory.create(), source, target);
+ }
+ }
+
+ return graph;
+ }
+}