View Javadoc
1   /*
2    * Copyright (c) 2003, The JUNG Authors 
3    *
4    * All rights reserved.
5    *
6    * This software is open-source under the BSD license; see either
7    * "license.txt" or
8    * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9    */
10  package edu.uci.ics.jung.algorithms.generators.random;
11  
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.HashMap;
15  import java.util.HashSet;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Random;
19  import java.util.Set;
20  
21  import com.google.common.base.Preconditions;
22  import com.google.common.base.Supplier;
23  
24  import edu.uci.ics.jung.algorithms.generators.EvolvingGraphGenerator;
25  import edu.uci.ics.jung.graph.Graph;
26  import edu.uci.ics.jung.graph.MultiGraph;
27  import edu.uci.ics.jung.graph.util.EdgeType;
28  import edu.uci.ics.jung.graph.util.Pair;
29  
30  
31  /**
32   * <p>Simple evolving scale-free random graph generator. At each time
33   * step, a new vertex is created and is connected to existing vertices
34   * according to the principle of "preferential attachment", whereby 
35   * vertices with higher degree have a higher probability of being 
36   * selected for attachment.
37   * 
38   * <p>At a given timestep, the probability <code>p</code> of creating an edge
39   * between an existing vertex <code>v</code> and the newly added vertex is
40   * <pre>
41   * p = (degree(v) + 1) / (|E| + |V|);
42   * </pre>
43   * 
44   * <p>where <code>|E|</code> and <code>|V|</code> are, respectively, the number 
45   * of edges and vertices currently in the network (counting neither the new
46   * vertex nor the other edges that are being attached to it).
47   * 
48   * <p>Note that the formula specified in the original paper
49   * (cited below) was
50   * <pre>
51   * p = degree(v) / |E|
52   * </pre>
53   * 
54   * 
55   * <p>However, this would have meant that the probability of attachment for any existing
56   * isolated vertex would be 0.  This version uses Lagrangian smoothing to give
57   * each existing vertex a positive attachment probability.
58   * 
59   * <p>The graph created may be either directed or undirected (controlled by a constructor
60   * parameter); the default is undirected.  
61   * If the graph is specified to be directed, then the edges added will be directed
62   * from the newly added vertex u to the existing vertex v, with probability proportional to the 
63   * indegree of v (number of edges directed towards v).  If the graph is specified to be undirected,
64   * then the (undirected) edges added will connect u to v, with probability proportional to the 
65   * degree of v. 
66   * 
67   * <p>The <code>parallel</code> constructor parameter specifies whether parallel edges
68   * may be created.
69   * 
70   * @see "A.-L. Barabasi and R. Albert, Emergence of scaling in random networks, Science 286, 1999."
71   * @author Scott White
72   * @author Joshua O'Madadhain
73   * @author Tom Nelson - adapted to jung2
74   */
75  public class BarabasiAlbertGenerator<V,E> implements EvolvingGraphGenerator<V,E> {
76      private Graph<V, E> mGraph = null;
77      private int mNumEdgesToAttachPerStep;
78      private int mElapsedTimeSteps;
79      private Random mRandom;
80      protected List<V> vertex_index;
81      protected int init_vertices;
82      protected Map<V,Integer> index_vertex;
83      protected Supplier<Graph<V,E>> graphFactory;
84      protected Supplier<V> vertexFactory;
85      protected Supplier<E> edgeFactory;
86      
87      /**
88       * Constructs a new instance of the generator.
89       * 
90       * @param graphFactory factory for graphs of the appropriate type
91       * @param vertexFactory factory for vertices of the appropriate type
92       * @param edgeFactory factory for edges of the appropriate type
93       * @param init_vertices     number of unconnected 'seed' vertices that the graph should start with
94       * @param numEdgesToAttach the number of edges that should be attached from the
95       * new vertex to pre-existing vertices at each time step
96       * @param seed  random number seed
97       * @param seedVertices storage for the seed vertices that this graph creates
98       */
99      // TODO: seedVertices is a bizarre way of exposing that information, refactor
100     public BarabasiAlbertGenerator(Supplier<Graph<V,E>> graphFactory,
101     		Supplier<V> vertexFactory, Supplier<E> edgeFactory, 
102     		int init_vertices, int numEdgesToAttach, 
103             int seed, Set<V> seedVertices)
104     {
105         Preconditions.checkArgument(init_vertices > 0,
106             "Number of initial unconnected 'seed' vertices must be positive");
107         Preconditions.checkArgument(numEdgesToAttach > 0, 
108             "Number of edges to attach at each time step must be positive");
109         
110         mNumEdgesToAttachPerStep = numEdgesToAttach;
111         mRandom = new Random(seed);
112         this.graphFactory = graphFactory;
113         this.vertexFactory = vertexFactory;
114         this.edgeFactory = edgeFactory;
115         this.init_vertices = init_vertices;
116         initialize(seedVertices);
117     }
118     
119 
120     /**
121      * Constructs a new instance of the generator, whose output will be an undirected graph,
122      * and which will use the current time as a seed for the random number generation.
123      * 
124      * @param graphFactory factory for graphs of the appropriate type
125      * @param vertexFactory factory for vertices of the appropriate type
126      * @param edgeFactory factory for edges of the appropriate type
127      * @param init_vertices     number of vertices that the graph should start with
128      * @param numEdgesToAttach the number of edges that should be attached from the
129      * new vertex to pre-existing vertices at each time step
130      * @param seedVertices storage for the seed vertices that this graph creates
131      */
132     public BarabasiAlbertGenerator(Supplier<Graph<V,E>> graphFactory, 
133     		Supplier<V> vertexFactory, Supplier<E> edgeFactory,
134     		int init_vertices, int numEdgesToAttach, Set<V> seedVertices) {
135         this(graphFactory, vertexFactory, edgeFactory, init_vertices, numEdgesToAttach, (int) System.currentTimeMillis(), seedVertices);
136     }
137     
138     private void initialize(Set<V> seedVertices) {
139     	
140     	mGraph = graphFactory.get();
141 
142         vertex_index = new ArrayList<V>(2*init_vertices);
143         index_vertex = new HashMap<V, Integer>(2*init_vertices);
144         for (int i = 0; i < init_vertices; i++) {
145             V v = vertexFactory.get();
146             mGraph.addVertex(v);
147             vertex_index.add(v);
148             index_vertex.put(v, i);
149             seedVertices.add(v);
150         }
151             
152         mElapsedTimeSteps = 0;
153     }
154 
155     private void createRandomEdge(Collection<V> preexistingNodes,
156     		V newVertex, Set<Pair<V>> added_pairs) {
157         V attach_point;
158         boolean created_edge = false;
159         Pair<V> endpoints;
160         do {
161             attach_point = vertex_index.get(mRandom.nextInt(vertex_index.size()));
162             
163             endpoints = new Pair<V>(newVertex, attach_point);
164             
165             // if parallel edges are not allowed, skip attach_point if <newVertex, attach_point>
166             // already exists; note that because of the way edges are added, we only need to check
167             // the list of candidate edges for duplicates.
168             if (!(mGraph instanceof MultiGraph))
169             {
170             	if (added_pairs.contains(endpoints))
171             		continue;
172             	if (mGraph.getDefaultEdgeType() == EdgeType.UNDIRECTED && 
173             		added_pairs.contains(new Pair<V>(attach_point, newVertex)))
174             		continue;
175             }
176 
177             double degree = mGraph.inDegree(attach_point);
178             
179             // subtract 1 from numVertices because we don't want to count newVertex
180             // (which has already been added to the graph, but not to vertex_index)
181             double attach_prob = (degree + 1) / (mGraph.getEdgeCount() + mGraph.getVertexCount() - 1);
182             if (attach_prob >= mRandom.nextDouble())
183                 created_edge = true;
184         }
185         while (!created_edge);
186 
187         added_pairs.add(endpoints);
188         
189         if (mGraph.getDefaultEdgeType() == EdgeType.UNDIRECTED) {
190         	added_pairs.add(new Pair<V>(attach_point, newVertex));
191         }
192     }
193 
194     public void evolveGraph(int numTimeSteps) {
195 
196         for (int i = 0; i < numTimeSteps; i++) {
197             evolveGraph();
198             mElapsedTimeSteps++;
199         }
200     }
201 
202     private void evolveGraph() {
203         Collection<V> preexistingNodes = mGraph.getVertices();
204         V newVertex = vertexFactory.get();
205 
206         mGraph.addVertex(newVertex);
207 
208         // generate and store the new edges; don't add them to the graph
209         // yet because we don't want to bias the degree calculations
210         // (all new edges in a timestep should be added in parallel)
211         Set<Pair<V>> added_pairs = new HashSet<Pair<V>>(mNumEdgesToAttachPerStep*3);
212         
213         for (int i = 0; i < mNumEdgesToAttachPerStep; i++) 
214         	createRandomEdge(preexistingNodes, newVertex, added_pairs);
215         
216         for (Pair<V> pair : added_pairs)
217         {
218         	V v1 = pair.getFirst();
219         	V v2 = pair.getSecond();
220         	if (mGraph.getDefaultEdgeType() != EdgeType.UNDIRECTED || 
221         			!mGraph.isNeighbor(v1, v2))
222         		mGraph.addEdge(edgeFactory.get(), pair);
223         }
224         // now that we're done attaching edges to this new vertex, 
225         // add it to the index
226         vertex_index.add(newVertex);
227         index_vertex.put(newVertex, new Integer(vertex_index.size() - 1));
228     }
229 
230     public int numIterations() {
231         return mElapsedTimeSteps;
232     }
233 
234     public Graph<V, E> get() {
235         return mGraph;
236     }
237 }