edu.uci.ics.jung.algorithms.metrics.TriadicCensus Maven / Gradle / Ivy
Show all versions of jung-algorithms Show documentation
/*
* Copyright (c) 2003, The JUNG Authors
*
* All rights reserved.
*
* This software is open-source under the BSD license; see either
* "license.txt" or
* https://github.com/jrtom/jung/blob/master/LICENSE for a description.
*/
package edu.uci.ics.jung.algorithms.metrics;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.Graph;
/**
* TriadicCensus is a standard social network tool that counts, for each of the
* different possible configurations of three vertices, the number of times
* that that configuration occurs in the given graph.
* This may then be compared to the set of expected counts for this particular
* graph or to an expected sample. This is often used in p* modeling.
*
* To use this class,
*
* long[] triad_counts = TriadicCensus(dg);
*
* where dg
is a DirectedGraph
.
* ith element of the array (for i in [1,16]) is the number of
* occurrences of the corresponding triad type.
* (The 0th element is not meaningful; this array is effectively 1-based.)
* To get the name of the ith triad (e.g. "003"),
* look at the global constant array c.TRIAD_NAMES[i]
*
* Triads are named as
* (number of pairs that are mutually tied)
* (number of pairs that are one-way tied)
* (number of non-tied pairs)
* in the triple. Since there are be only three pairs, there is a finite
* set of these possible triads.
*
* In fact, there are exactly 16, conventionally sorted by the number of
* realized edges in the triad:
*
* Descriptions of the different types of triads
* Number Configuration Notes
* 1 003 The empty triad
* 2 012
* 3 102
* 4 021D "Down": the directed edges point away
* 5 021U "Up": the directed edges meet
* 6 021C "Circle": one in, one out
* 7 111D "Down": 021D but one edge is mutual
* 8 111U "Up": 021U but one edge is mutual
* 9 030T "Transitive": two point to the same vertex
* 10 030C "Circle": A→B→C→A
* 11 201
* 12 120D "Down": 021D but the third edge is mutual
* 13 120U "Up": 021U but the third edge is mutual
* 14 120C "Circle": 021C but the third edge is mutual
* 15 210
* 16 300 The complete
*
*
* This implementation takes O( m ), m is the number of edges in the graph.
*
* It is based on
*
* A subquadratic triad census algorithm for large sparse networks
* with small maximum degree
* Vladimir Batagelj and Andrej Mrvar, University of Ljubljana
* Published in Social Networks.
* @author Danyel Fisher
* @author Tom Nelson - converted to jung2
*
*/
public class TriadicCensus {
// NOTE THAT THIS RETURNS STANDARD 1-16 COUNT!
// and their types
public static final String[] TRIAD_NAMES = { "N/A", "003", "012", "102", "021D",
"021U", "021C", "111D", "111U", "030T", "030C", "201", "120D",
"120U", "120C", "210", "300" };
public static final int MAX_TRIADS = TRIAD_NAMES.length;
/**
* Returns an array whose ith element (for i in [1,16]) is the number of
* occurrences of the corresponding triad type in g
.
* (The 0th element is not meaningful; this array is effectively 1-based.)
*
* @param g the graph whose properties are being measured
* @param the vertex type
* @param the edge type
* @return an array encoding the number of occurrences of each triad type
*/
public static long[] getCounts(DirectedGraph g) {
long[] count = new long[MAX_TRIADS];
List id = new ArrayList(g.getVertices());
// apply algorithm to each edge, one at at time
for (int i_v = 0; i_v < g.getVertexCount(); i_v++) {
V v = id.get(i_v);
for(V u : g.getNeighbors(v)) {
int triType = -1;
if (id.indexOf(u) <= i_v)
continue;
Set neighbors = new HashSet(g.getNeighbors(u));
neighbors.addAll(g.getNeighbors(v));
neighbors.remove(u);
neighbors.remove(v);
if (g.isSuccessor(v,u) && g.isSuccessor(u,v)) {
triType = 3;
} else {
triType = 2;
}
count[triType] += g.getVertexCount() - neighbors.size() - 2;
for (V w : neighbors) {
if (shouldCount(g, id, u, v, w)) {
count [ triType ( triCode(g, u, v, w) ) ] ++;
}
}
}
}
int sum = 0;
for (int i = 2; i <= 16; i++) {
sum += count[i];
}
int n = g.getVertexCount();
count[1] = n * (n-1) * (n-2) / 6 - sum;
return count;
}
/**
* This is the core of the technique in the paper. Returns an int from 0 to
* 63 which encodes the presence of all possible links between u, v, and w
* as bit flags: WU = 32, UW = 16, WV = 8, VW = 4, UV = 2, VU = 1
*
* @param g the graph for which the calculation is being made
* @param u a vertex in g
* @param v a vertex in g
* @param w a vertex in g
* @param the vertex type
* @param the edge type
* @return an int encoding the presence of all links between u, v, and w
*/
public static int triCode(Graph g, V u, V v, V w) {
int i = 0;
i += link(g, v, u ) ? 1 : 0;
i += link(g, u, v ) ? 2 : 0;
i += link(g, v, w ) ? 4 : 0;
i += link(g, w, v ) ? 8 : 0;
i += link(g, u, w ) ? 16 : 0;
i += link(g, w, u ) ? 32 : 0;
return i;
}
protected static boolean link(Graph g, V a, V b) {
return g.isPredecessor(b, a);
}
/**
* @param triCode the code returned by {@code triCode()}
* @return the string code associated with the numeric type
*/
public static int triType( int triCode ) {
return codeToType[ triCode ];
}
/**
* For debugging purposes, this is copied straight out of the paper which
* means that they refer to triad types 1-16.
*/
protected static final int[] codeToType = { 1, 2, 2, 3, 2, 4, 6, 8, 2, 6, 5, 7, 3, 8,
7, 11, 2, 6, 4, 8, 5, 9, 9, 13, 6, 10, 9, 14, 7, 14, 12, 15, 2, 5,
6, 7, 6, 9, 10, 14, 4, 9, 9, 12, 8, 13, 14, 15, 3, 7, 8, 11, 7, 12,
14, 15, 8, 14, 13, 15, 11, 15, 15, 16 };
/**
* Return true iff this ordering is canonical and therefore we should build statistics for it.
*
* @param g the graph whose properties are being examined
* @param id a list of the vertices in g; used to assign an index to each
* @param u a vertex in g
* @param v a vertex in g
* @param w a vertex in g
* @param the vertex type
* @param the edge type
* @return true if index(u) < index(w), or if index(v) < index(w) < index(u)
* and v doesn't link to w; false otherwise
*/
protected static boolean shouldCount(Graph g, List id, V u, V v, V w) {
int i_u = id.indexOf(u);
int i_w = id.indexOf(w);
if (i_u < i_w)
return true;
int i_v = id.indexOf(v);
if ((i_v < i_w) && (i_w < i_u) && (!g.isNeighbor(w,v)))
return true;
return false;
}
}