2 * Copyright (c) 2003, the JUNG Project and the Regents of the University
6 * This software is open-source under the BSD license; see either
8 * http://jung.sourceforge.net/license.txt for a description.
10 package edu.uci.ics.jung.algorithms.generators.random;
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;
18 import java.util.Random;
21 import org.apache.commons.collections15.Factory;
23 import edu.uci.ics.jung.algorithms.generators.EvolvingGraphGenerator;
24 import edu.uci.ics.jung.graph.Graph;
25 import edu.uci.ics.jung.graph.MultiGraph;
26 import edu.uci.ics.jung.graph.util.EdgeType;
27 import edu.uci.ics.jung.graph.util.Pair;
31 * <p>Simple evolving scale-free random graph generator. At each time
32 * step, a new vertex is created and is connected to existing vertices
33 * according to the principle of "preferential attachment", whereby
34 * vertices with higher degree have a higher probability of being
35 * selected for attachment.</p>
37 * <p>At a given timestep, the probability <code>p</code> of creating an edge
38 * between an existing vertex <code>v</code> and the newly added vertex is
40 * p = (degree(v) + 1) / (|E| + |V|);
43 * <p>where <code>|E|</code> and <code>|V|</code> are, respectively, the number
44 * of edges and vertices currently in the network (counting neither the new
45 * vertex nor the other edges that are being attached to it).</p>
47 * <p>Note that the formula specified in the original paper
54 * <p>However, this would have meant that the probability of attachment for any existing
55 * isolated vertex would be 0. This version uses Lagrangian smoothing to give
56 * each existing vertex a positive attachment probability.</p>
58 * <p>The graph created may be either directed or undirected (controlled by a constructor
59 * parameter); the default is undirected.
60 * If the graph is specified to be directed, then the edges added will be directed
61 * from the newly added vertex u to the existing vertex v, with probability proportional to the
62 * indegree of v (number of edges directed towards v). If the graph is specified to be undirected,
63 * then the (undirected) edges added will connect u to v, with probability proportional to the
66 * <p>The <code>parallel</code> constructor parameter specifies whether parallel edges
69 * @see "A.-L. Barabasi and R. Albert, Emergence of scaling in random networks, Science 286, 1999."
71 * @author Joshua O'Madadhain
72 * @author Tom Nelson - adapted to jung2
74 public class BarabasiAlbertGenerator<V,E> implements EvolvingGraphGenerator<V,E> {
75 private Graph<V, E> mGraph = null;
76 private int mNumEdgesToAttachPerStep;
77 private int mElapsedTimeSteps;
78 private Random mRandom;
79 protected List<V> vertex_index;
80 protected int init_vertices;
81 protected Map<V,Integer> index_vertex;
82 protected Factory<Graph<V,E>> graphFactory;
83 protected Factory<V> vertexFactory;
84 protected Factory<E> edgeFactory;
87 * Constructs a new instance of the generator.
88 * @param init_vertices number of unconnected 'seed' vertices that the graph should start with
89 * @param numEdgesToAttach the number of edges that should be attached from the
90 * new vertex to pre-existing vertices at each time step
91 * @param directed specifies whether the graph and edges to be created should be directed or not
92 * @param parallel specifies whether the algorithm permits parallel edges
93 * @param seed random number seed
95 public BarabasiAlbertGenerator(Factory<Graph<V,E>> graphFactory,
96 Factory<V> vertexFactory, Factory<E> edgeFactory,
97 int init_vertices, int numEdgesToAttach,
98 int seed, Set<V> seedVertices)
100 assert init_vertices > 0 : "Number of initial unconnected 'seed' vertices " +
102 assert numEdgesToAttach > 0 : "Number of edges to attach " +
103 "at each time step must be positive";
105 mNumEdgesToAttachPerStep = numEdgesToAttach;
106 mRandom = new Random(seed);
107 this.graphFactory = graphFactory;
108 this.vertexFactory = vertexFactory;
109 this.edgeFactory = edgeFactory;
110 this.init_vertices = init_vertices;
111 initialize(seedVertices);
116 * Constructs a new instance of the generator, whose output will be an undirected graph,
117 * and which will use the current time as a seed for the random number generation.
118 * @param init_vertices number of vertices that the graph should start with
119 * @param numEdgesToAttach the number of edges that should be attached from the
120 * new vertex to pre-existing vertices at each time step
122 public BarabasiAlbertGenerator(Factory<Graph<V,E>> graphFactory,
123 Factory<V> vertexFactory, Factory<E> edgeFactory,
124 int init_vertices, int numEdgesToAttach, Set<V> seedVertices) {
125 this(graphFactory, vertexFactory, edgeFactory, init_vertices, numEdgesToAttach, (int) System.currentTimeMillis(), seedVertices);
128 private void initialize(Set<V> seedVertices) {
130 mGraph = graphFactory.create();
132 vertex_index = new ArrayList<V>(2*init_vertices);
133 index_vertex = new HashMap<V, Integer>(2*init_vertices);
134 for (int i = 0; i < init_vertices; i++) {
135 V v = vertexFactory.create();
138 index_vertex.put(v, i);
142 mElapsedTimeSteps = 0;
145 private void createRandomEdge(Collection<V> preexistingNodes,
146 V newVertex, Set<Pair<V>> added_pairs) {
148 boolean created_edge = false;
151 attach_point = vertex_index.get(mRandom.nextInt(vertex_index.size()));
153 endpoints = new Pair<V>(newVertex, attach_point);
155 // if parallel edges are not allowed, skip attach_point if <newVertex, attach_point>
156 // already exists; note that because of the way edges are added, we only need to check
157 // the list of candidate edges for duplicates.
158 if (!(mGraph instanceof MultiGraph))
160 if (added_pairs.contains(endpoints))
162 if (mGraph.getDefaultEdgeType() == EdgeType.UNDIRECTED &&
163 added_pairs.contains(new Pair<V>(attach_point, newVertex)))
167 double degree = mGraph.inDegree(attach_point);
169 // subtract 1 from numVertices because we don't want to count newVertex
170 // (which has already been added to the graph, but not to vertex_index)
171 double attach_prob = (degree + 1) / (mGraph.getEdgeCount() + mGraph.getVertexCount() - 1);
172 if (attach_prob >= mRandom.nextDouble())
175 while (!created_edge);
177 added_pairs.add(endpoints);
179 if (mGraph.getDefaultEdgeType() == EdgeType.UNDIRECTED) {
180 added_pairs.add(new Pair<V>(attach_point, newVertex));
184 public void evolveGraph(int numTimeSteps) {
186 for (int i = 0; i < numTimeSteps; i++) {
192 private void evolveGraph() {
193 Collection<V> preexistingNodes = mGraph.getVertices();
194 V newVertex = vertexFactory.create();
196 mGraph.addVertex(newVertex);
198 // generate and store the new edges; don't add them to the graph
199 // yet because we don't want to bias the degree calculations
200 // (all new edges in a timestep should be added in parallel)
201 Set<Pair<V>> added_pairs = new HashSet<Pair<V>>(mNumEdgesToAttachPerStep*3);
203 for (int i = 0; i < mNumEdgesToAttachPerStep; i++)
204 createRandomEdge(preexistingNodes, newVertex, added_pairs);
206 for (Pair<V> pair : added_pairs)
208 V v1 = pair.getFirst();
209 V v2 = pair.getSecond();
210 if (mGraph.getDefaultEdgeType() != EdgeType.UNDIRECTED ||
211 !mGraph.isNeighbor(v1, v2))
212 mGraph.addEdge(edgeFactory.create(), pair);
214 // now that we're done attaching edges to this new vertex,
215 // add it to the index
216 vertex_index.add(newVertex);
217 index_vertex.put(newVertex, new Integer(vertex_index.size() - 1));
220 public int numIterations() {
221 return mElapsedTimeSteps;
224 public Graph<V, E> create() {