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.metrics;
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.List;
17 import org.apache.commons.collections15.CollectionUtils;
19 import edu.uci.ics.jung.graph.DirectedGraph;
20 import edu.uci.ics.jung.graph.Graph;
24 * TriadicCensus is a standard social network tool that counts, for each of the
25 * different possible configurations of three vertices, the number of times
26 * that that configuration occurs in the given graph.
27 * This may then be compared to the set of expected counts for this particular
28 * graph or to an expected sample. This is often used in p* modeling.
32 * long[] triad_counts = TriadicCensus(dg);
34 * where <code>dg</code> is a <code>DirectedGraph</code>.
35 * ith element of the array (for i in [1,16]) is the number of
36 * occurrences of the corresponding triad type.
37 * (The 0th element is not meaningful; this array is effectively 1-based.)
38 * To get the name of the ith triad (e.g. "003"),
39 * look at the global constant array c.TRIAD_NAMES[i]
42 * (number of pairs that are mutually tied)
43 * (number of pairs that are one-way tied)
44 * (number of non-tied pairs)
45 * in the triple. Since there are be only three pairs, there is a finite
46 * set of these possible triads.
48 * In fact, there are exactly 16, conventionally sorted by the number of
49 * realized edges in the triad:
51 * <tr><th>Number</th> <th>Configuration</th> <th>Notes</th></tr>
52 * <tr><td>1</td><td>003</td><td>The empty triad</td></tr>
53 * <tr><td>2</td><td>012</td><td></td></tr>
54 * <tr><td>3</td><td>102</td><td></td></tr>
55 * <tr><td>4</td><td>021D</td><td>"Down": the directed edges point away</td></tr>
56 * <tr><td>5</td><td>021U</td><td>"Up": the directed edges meet</td></tr>
57 * <tr><td>6</td><td>021C</td><td>"Circle": one in, one out</td></tr>
58 * <tr><td>7</td><td>111D</td><td>"Down": 021D but one edge is mutual</td></tr>
59 * <tr><td>8</td><td>111U</td><td>"Up": 021U but one edge is mutual</td></tr>
60 * <tr><td>9</td><td>030T</td><td>"Transitive": two point to the same vertex</td></tr>
61 * <tr><td>10</td><td>030C</td><td>"Circle": A->B->C->A</td></tr>
62 * <tr><td>11</td><td>201</td><td></td></tr>
63 * <tr><td>12</td><td>120D</td><td>"Down": 021D but the third edge is mutual</td></tr>
64 * <tr><td>13</td><td>120U</td><td>"Up": 021U but the third edge is mutual</td></tr>
65 * <tr><td>14</td><td>120C</td><td>"Circle": 021C but the third edge is mutual</td></tr>
66 * <tr><td>15</td><td>210</td><td></td></tr>
67 * <tr><td>16</td><td>300</td><td>The complete</td></tr>
70 * This implementation takes O( m ), m is the number of edges in the graph.
73 * <a href="http://vlado.fmf.uni-lj.si/pub/networks/doc/triads/triads.pdf">
74 * A subquadratic triad census algorithm for large sparse networks
75 * with small maximum degree</a>
76 * Vladimir Batagelj and Andrej Mrvar, University of Ljubljana
77 * Published in Social Networks.
78 * @author Danyel Fisher
79 * @author Tom Nelson - converted to jung2
82 public class TriadicCensus {
84 // NOTE THAT THIS RETURNS STANDARD 1-16 COUNT!
87 public static final String[] TRIAD_NAMES = { "N/A", "003", "012", "102", "021D",
88 "021U", "021C", "111D", "111U", "030T", "030C", "201", "120D",
89 "120U", "120C", "210", "300" };
91 public static final int MAX_TRIADS = TRIAD_NAMES.length;
94 * Returns an array whose ith element (for i in [1,16]) is the number of
95 * occurrences of the corresponding triad type in <code>g</code>.
96 * (The 0th element is not meaningful; this array is effectively 1-based.)
100 public static <V,E> long[] getCounts(DirectedGraph<V,E> g) {
101 long[] count = new long[MAX_TRIADS];
103 List<V> id = new ArrayList<V>(g.getVertices());
105 // apply algorithm to each edge, one at at time
106 for (int i_v = 0; i_v < g.getVertexCount(); i_v++) {
108 for(V u : g.getNeighbors(v)) {
110 if (id.indexOf(u) <= i_v)
112 Set<V> neighbors = new HashSet<V>(CollectionUtils.union(g.getNeighbors(u), g.getNeighbors(v)));
115 if (g.isSuccessor(v,u) && g.isSuccessor(u,v)) {
120 count[triType] += g.getVertexCount() - neighbors.size() - 2;
121 for (V w : neighbors) {
122 if (shouldCount(g, id, u, v, w)) {
123 count [ triType ( triCode(g, u, v, w) ) ] ++;
129 for (int i = 2; i <= 16; i++) {
132 int n = g.getVertexCount();
133 count[1] = n * (n-1) * (n-2) / 6 - sum;
138 * This is the core of the technique in the paper. Returns an int from 0 to
139 * 65 based on: WU -> 32 UW -> 16 WV -> 8 VW -> 4 UV -> 2 VU -> 1
142 public static <V,E> int triCode(Graph<V,E> g, V u, V v, V w) {
144 i += link(g, v, u ) ? 1 : 0;
145 i += link(g, u, v ) ? 2 : 0;
146 i += link(g, v, w ) ? 4 : 0;
147 i += link(g, w, v ) ? 8 : 0;
148 i += link(g, u, w ) ? 16 : 0;
149 i += link(g, w, u ) ? 32 : 0;
153 protected static <V,E> boolean link(Graph<V,E> g, V a, V b) {
154 return g.isPredecessor(b, a);
159 * Simply returns the triCode.
161 * @return the string code associated with the numeric type
163 public static int triType( int triCode ) {
164 return codeToType[ triCode ];
168 * For debugging purposes, this is copied straight out of the paper which
169 * means that they refer to triad types 1-16.
171 protected static final int[] codeToType = { 1, 2, 2, 3, 2, 4, 6, 8, 2, 6, 5, 7, 3, 8,
172 7, 11, 2, 6, 4, 8, 5, 9, 9, 13, 6, 10, 9, 14, 7, 14, 12, 15, 2, 5,
173 6, 7, 6, 9, 10, 14, 4, 9, 9, 12, 8, 13, 14, 15, 3, 7, 8, 11, 7, 12,
174 14, 15, 8, 14, 13, 15, 11, 15, 15, 16 };
177 * Make sure we have a canonical ordering: Returns true if u < w, or v < w <
178 * u and v doesn't link to w
184 * @return true if u < w, or if v < w < u and v doesn't link to w; false otherwise
186 protected static <V,E> boolean shouldCount(Graph<V,E> g, List<V> id, V u, V v, V w) {
187 int i_u = id.indexOf(u);
188 int i_w = id.indexOf(w);
191 int i_v = id.indexOf(v);
192 if ((i_v < i_w) && (i_w < i_u) && (!g.isNeighbor(w,v)))