View Javadoc
1   /*
2    * Created on Sep 19, 2005
3    *
4    * Copyright (c) 2005, The JUNG Authors 
5    *
6    * All rights reserved.
7    *
8    * This software is open-source under the BSD license; see either
9    * "license.txt" or
10   * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
11   */
12  package edu.uci.ics.jung.algorithms.metrics;
13  
14  import com.google.common.base.Function;
15  
16  import edu.uci.ics.jung.graph.Graph;
17  
18  /**
19   * Calculates some of the measures from Burt's text "Structural Holes: 
20   * The Social Structure of Competition".
21   * 
22   * <p><b>Notes</b>: 
23   * <ul>
24   * <li>Each of these measures assumes that each edge has an associated 
25   * non-null weight whose value is accessed through the specified 
26   * <code>Transformer</code> instance.
27   * <li>Nonexistent edges are treated as edges with weight 0 for purposes 
28   * of edge weight calculations.
29   * </ul>
30   *  
31   * <p>Based on code donated by Jasper Voskuilen and 
32   * Diederik van Liere of the Department of Information and Decision Sciences
33   * at Erasmus University.
34   * 
35   * @author Joshua O'Madadhain
36   * @author Jasper Voskuilen
37   * @see "Ronald Burt, Structural Holes: The Social Structure of Competition"
38   * @author Tom Nelson - converted to jung2
39   */
40  public class StructuralHoles<V,E> {
41  	
42      protected Function<E, ? extends Number> edge_weight;
43      protected Graph<V,E> g;
44      
45      /**
46       * @param graph the graph for which the metrics are to be calculated
47       * @param nev the edge weights
48       */
49      public StructuralHoles(Graph<V,E> graph, Function<E, ? extends Number> nev) 
50      {
51          this.g = graph;
52          this.edge_weight = nev;
53      }
54  
55      /**
56       * Burt's measure of the effective size of a vertex's network.  Essentially, the
57       * number of neighbors minus the average degree of those in <code>v</code>'s neighbor set,
58       * not counting ties to <code>v</code>.  Formally: 
59       * <pre>
60       * effectiveSize(v) = v.degree() - (sum_{u in N(v)} sum_{w in N(u), w !=u,v} p(v,w)*m(u,w))
61       * </pre>
62       * where 
63       * <ul>
64       * <li><code>N(a) = a.getNeighbors()</code>
65       * <li><code>p(v,w) =</code> normalized mutual edge weight of v and w
66       * <li><code>m(u,w)</code> = maximum-scaled mutual edge weight of u and w
67       * </ul>
68       * @param v the vertex whose properties are being measured
69       * @return the effective size of the vertex's network
70       * 
71       * @see #normalizedMutualEdgeWeight(Object, Object)
72       * @see #maxScaledMutualEdgeWeight(Object, Object) 
73       */
74      public double effectiveSize(V v)
75      {
76          double result = g.degree(v);
77          for(V u : g.getNeighbors(v)) {
78  
79              for(V w : g.getNeighbors(u)) {
80  
81                  if (w != v && w != u)
82                      result -= normalizedMutualEdgeWeight(v,w) * 
83                                maxScaledMutualEdgeWeight(u,w);
84              }
85          }
86          return result;
87      }
88      
89      /**
90       * Returns the effective size of <code>v</code> divided by the number of 
91       * alters in <code>v</code>'s network.  (In other words, 
92       * <code>effectiveSize(v) / v.degree()</code>.)
93       * If <code>v.degree() == 0</code>, returns 0.
94       * 
95       * @param v the vertex whose properties are being measured
96       * @return the effective size of the vertex divided by its degree
97       */
98      public double efficiency(V v) {
99          double degree = g.degree(v);
100         
101         if (degree == 0)
102             return 0;
103         else
104             return effectiveSize(v) / degree;
105     }
106 
107     /**
108      * Burt's constraint measure (equation 2.4, page 55 of Burt, 1992). Essentially a
109      * measure of the extent to which <code>v</code> is invested in people who are invested in
110      * other of <code>v</code>'s alters (neighbors).  The "constraint" is characterized
111      * by a lack of primary holes around each neighbor.  Formally:
112      * <pre>
113      * constraint(v) = sum_{w in MP(v), w != v} localConstraint(v,w)
114      * </pre>
115      * where MP(v) is the subset of v's neighbors that are both predecessors and successors of v. 
116      * @see #localConstraint(Object, Object)
117      * 
118      * @param v the vertex whose properties are being measured
119      * @return the constraint of the vertex
120      */
121     public double constraint(V v) {
122         double result = 0;
123         for(V w : g.getSuccessors(v)) {
124 
125             if (v != w && g.isPredecessor(v,w))
126             {
127                 result += localConstraint(v, w);
128             }
129         }
130 
131         return result;
132     }
133 
134     
135     /**
136      * Calculates the hierarchy value for a given vertex.  Returns <code>NaN</code> when
137      * <code>v</code>'s degree is 0, and 1 when <code>v</code>'s degree is 1.
138      * Formally:
139      * <pre>
140      * hierarchy(v) = (sum_{v in N(v), w != v} s(v,w) * log(s(v,w))}) / (v.degree() * Math.log(v.degree()) 
141      * </pre>
142      * where
143      * <ul>
144      * <li><code>N(v) = v.getNeighbors()</code> 
145      * <li><code>s(v,w) = localConstraint(v,w) / (aggregateConstraint(v) / v.degree())</code>
146      * </ul>
147      * @see #localConstraint(Object, Object)
148      * @see #aggregateConstraint(Object)
149      * 
150      * @param v the vertex whose properties are being measured
151      * @return the hierarchy value for a given vertex
152      */
153     public double hierarchy(V v)
154     {
155         double v_degree = g.degree(v);
156         
157         if (v_degree == 0)
158             return Double.NaN;
159         if (v_degree == 1)
160             return 1;
161         
162         double v_constraint = aggregateConstraint(v);
163 
164         double numerator = 0;
165         for (V w : g.getNeighbors(v)) {
166         
167             if (v != w)
168             {
169                 double sl_constraint = localConstraint(v, w) / (v_constraint / v_degree);
170                 numerator += sl_constraint * Math.log(sl_constraint);
171             }
172         }
173 
174         return numerator / (v_degree * Math.log(v_degree));
175     }
176 
177     /**
178      * Returns the local constraint on <code>v1</code> from a lack of primary holes 
179      * around its neighbor <code>v2</code>.
180      * Based on Burt's equation 2.4.  Formally:
181      * <pre>
182      * localConstraint(v1, v2) = ( p(v1,v2) + ( sum_{w in N(v)} p(v1,w) * p(w, v2) ) )^2
183      * </pre>
184      * where 
185      * <ul>
186      * <li><code>N(v) = v.getNeighbors()</code>
187      * <li><code>p(v,w) =</code> normalized mutual edge weight of v and w
188      * </ul>
189      * @param v1 the first vertex whose local constraint is desired
190      * @param v2 the second vertex whose local constraint is desired
191      * @return the local constraint on (v1, v2)
192      * @see #normalizedMutualEdgeWeight(Object, Object)
193      */
194     public double localConstraint(V v1, V v2) 
195     {
196         double nmew_vw = normalizedMutualEdgeWeight(v1, v2);
197         double inner_result = 0;
198         for (V w : g.getNeighbors(v1)) {
199 
200             inner_result += normalizedMutualEdgeWeight(v1,w) * 
201                 normalizedMutualEdgeWeight(w,v2);
202         }
203         return (nmew_vw + inner_result) * (nmew_vw + inner_result);
204     }
205     
206     /**
207      * The aggregate constraint on <code>v</code>.  Based on Burt's equation 2.7.  
208      * Formally:
209      * <pre>
210      * aggregateConstraint(v) = sum_{w in N(v)} localConstraint(v,w) * O(w)
211      * </pre>
212      * where
213      * <ul>
214      * <li><code>N(v) = v.getNeighbors()</code>
215      * <li><code>O(w) = organizationalMeasure(w)</code>
216      * </ul>
217      * 
218      * @param v the vertex whose properties are being measured
219      * @return the aggregate constraint on v
220      */
221     public double aggregateConstraint(V v)
222     {
223         double result = 0;
224         for (V w : g.getNeighbors(v)) {
225 
226             result += localConstraint(v, w) * organizationalMeasure(g, w);
227         }
228         return result;
229     }
230     
231     /**
232      * A measure of the organization of individuals within the subgraph 
233      * centered on <code>v</code>.  Burt's text suggests that this is 
234      * in some sense a measure of how "replaceable" <code>v</code> is by 
235      * some other element of this subgraph.  Should be a number in the
236      * closed interval [0,1].
237      * 
238      * <p>This implementation returns 1.  Users may wish to override this
239      * method in order to define their own behavior.
240      * @param g the subgraph centered on v
241      * @param v the vertex whose properties are being measured
242      * @return 1.0 (in this implementation)
243      */
244     protected double organizationalMeasure(Graph<V,E> g, V v) {
245         return 1.0;
246     }
247     
248    
249     /**
250      * Returns the proportion of <code>v1</code>'s network time and energy invested
251      * in the relationship with <code>v2</code>.  Formally:
252      * <pre>
253      * normalizedMutualEdgeWeight(a,b) = mutual_weight(a,b) / (sum_c mutual_weight(a,c))
254      * </pre>
255      * Returns 0 if either numerator or denominator = 0, or if <code>v1 == v2</code>.
256      * @see #mutualWeight(Object, Object)
257      * @param v1 the first vertex of the pair whose property is being measured
258      * @param v2 the second vertex of the pair whose property is being measured
259      * @return the normalized mutual edge weight between v1 and v2
260      */
261     protected double normalizedMutualEdgeWeight(V v1, V v2)
262     {
263         if (v1 == v2)
264             return 0;
265         
266         double numerator = mutualWeight(v1, v2);
267         
268         if (numerator == 0)
269             return 0;
270         
271         double denominator = 0;
272         for (V v : g.getNeighbors(v1)) {
273             denominator += mutualWeight(v1, v);
274         }
275         if (denominator == 0)
276             return 0;
277         
278         return numerator / denominator;
279     }
280     
281     /**
282      * Returns the weight of the edge from <code>v1</code> to <code>v2</code>
283      * plus the weight of the edge from <code>v2</code> to <code>v1</code>;
284      * if either edge does not exist, it is treated as an edge with weight 0. 
285      * Undirected edges are treated as two antiparallel directed edges (that
286      * is, if there is one undirected edge with weight <i>w</i> connecting 
287      * <code>v1</code> to <code>v2</code>, the value returned is 2<i>w</i>).
288      * Ignores parallel edges; if there are any such, one is chosen at random.
289      * Throws <code>NullPointerException</code> if either edge is 
290      * present but not assigned a weight by the constructor-specified
291      * <code>NumberEdgeValue</code>.
292      * 
293      * @param v1 the first vertex of the pair whose property is being measured
294      * @param v2 the second vertex of the pair whose property is being measured
295      * @return the weights of the edges {@code<v1, v2>} and {@code <v2, v1>}
296      */
297     protected double mutualWeight(V v1, V v2)
298     {
299         E e12 = g.findEdge(v1,v2);
300         E e21 = g.findEdge(v2,v1);
301         double w12 = (e12 != null ? edge_weight.apply(e12).doubleValue() : 0);
302         double w21 = (e21 != null ? edge_weight.apply(e21).doubleValue() : 0);
303         
304         return w12 + w21;
305     }
306     
307     /**
308      * The marginal strength of v1's relation with contact v2.
309      * Formally:
310      * <pre>
311      * normalized_mutual_weight = mutual_weight(a,b) / (max_c mutual_weight(a,c))
312      * </pre>
313      * Returns 0 if either numerator or denominator is 0, or if <code>v1 == v2</code>.
314      * 
315      * @param v1 the first vertex of the pair whose property is being measured
316      * @param v2 the second vertex of the pair whose property is being measured
317      * @return the marginal strength of v1's relation with v2
318      * 
319      * @see #mutualWeight(Object, Object)
320      */
321     protected double maxScaledMutualEdgeWeight(V v1, V v2)
322     {
323         if (v1 == v2)
324             return 0;
325 
326         double numerator = mutualWeight(v1, v2);
327 
328         if (numerator == 0)
329             return 0;
330         
331         double denominator = 0;
332         for (V w : g.getNeighbors(v1)) {
333 
334             if (v2 != w)
335                 denominator = Math.max(numerator, mutualWeight(v1, w));
336         }
337         
338         if (denominator == 0)
339             return 0;
340         
341         return numerator / denominator;
342     }
343 }