/* Copyright 2010 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.field.network.stats; import sim.field.network.*; import java.util.*; import sim.util.*; /** * @author Gabriel Catalin Balan * */ public class DyadTriadStatistics { public static final int TRIAD_003 = 0; public static final int TRIAD_012 = 1; public static final int TRIAD_102 = 2; public static final int TRIAD_021D = 3; public static final int TRIAD_021U = 4; public static final int TRIAD_021C = 5; public static final int TRIAD_111D = 6; public static final int TRIAD_111U = 7; public static final int TRIAD_030T = 8; public static final int TRIAD_030C = 9; public static final int TRIAD_201 = 10; public static final int TRIAD_120D = 11; public static final int TRIAD_120U = 12; public static final int TRIAD_120C = 13; public static final int TRIAD_210 = 14; public static final int TRIAD_300 = 15; public static final String[] MAN_TRIAD_CLASSES = { "003", "012", "102", "021D", "021U", "021C", "111D", "111U", "030T", "030C", "201", "120D", "120U", "120C", "210", "300" }; /** * Computes the triad census (Wasserman and Faust, pages 564-567). * It assumes a single asymmetric relation where weights are irrelevant. * @return 16-length int array * */ public static int[] triadCensus( final Network network) { int[] census = new int[16]; for(int i=0;i<16;i++) census[i]=0; int n = network.allNodes.numObjs; if(n<3) return census;// should I through an exception instead? Edge[][] adjMatrix = network.getAdjacencyMatrix(); //md, ad, nd= mutual, asymmetric and null dyads in the triad (MAN notation) //c = edge count in the triad //c_ij = edge count in dyad ij //m_ij, a_ij, n_ij = {1,0} depending on whether the ij dyad is M, A and N //in_i, out_i = in and out degree of node i //e_uv = {1,0} dependinf on whether there is an edge between u and v for(int i=0; i<n;i++) { Edge[] adj_i = adjMatrix[i]; for(int j=i+1;j<n;j++) { Edge[] adj_j = adjMatrix[j]; int e_ij=(adj_i[j]==null)?0:1; int e_ji=(adj_j[i]==null)?0:1; int c_ij; int m_ij=0; //// ad and nd are never used. Thy are not completelly deleted //// cause I might need them for debugging //// int a_ij=0, n_ij=0; //// switch (c_ij=e_ij+e_ji) //// { //// case 0: n_ij++;break; //// case 1: a_ij++;break; //// case 2: m_ij++;break; //// } if((c_ij=e_ij+e_ji)==2) m_ij++; for(int k=j+1;k<n;k++) { int md=m_ij; //// ad and nd are never used. Thy are not completelly deleted //// cause I might need them for debugging //// int ad=a_ij, nd=n_ij, int c=c_ij=e_ij+e_ji; Edge[] adj_k = adjMatrix[k]; int c_ik, c_jk; int e_ik = (adj_i[k]==null)?0:1; int e_jk = (adj_j[k]==null)?0:1; int e_ki = (adj_k[i]==null)?0:1; int e_kj = (adj_k[j]==null)?0:1; //// switch (c_ik=e_ik+e_ki) //// { //// case 0: nd++;break; //// case 1: ad++;break; //// case 2: md++;break; //// } if((c_ik=e_ik+e_ki)==2) md++; //// switch (c_jk=e_jk+e_kj) //// { //// case 0: nd++;break; //// case 1: ad++;break; //// case 2: md++;break; //// } if((c_jk=e_jk+e_kj)==2) md++; c+=c_ik+c_jk; switch (c) { case 0: census[TRIAD_003]++;break; case 1: census[TRIAD_012]++;break; case 2: if(md==1) { census[TRIAD_102]++; break; } // now I must decide between the three 021s (D, U and C) // f=sum out degree for the unconnected nodes //f(D) =0, f(U)=2, f(C)=1 // int f = (c_ij==0)?//i and j are "down" in the 021 MAN diagram e_jk+e_ik: (c_ik==0)?//i and k are "down" e_ij+e_kj: e_ji+e_ki; switch(f) { case 0: census[TRIAD_021D]++;break; case 1: census[TRIAD_021C]++;break; case 2: census[TRIAD_021U]++;break; } break; case 3: if(md==1) { //here's how I can tell the 111s apart D has <1,0> and U has <0,1> //(the other nodes are <1,1> and <1,2> in both cases. // //I use the <o,i> notation to say out degree and in-degree of a node. if(c_ij==2)//k is the discriminating node (either <0,1> or <1,0>) census[(e_ki+e_ki==0)?TRIAD_111U:TRIAD_111D]++; else if(c_ik==2)//look at j census[(e_ji+e_jk==0)?TRIAD_111U:TRIAD_111D]++; else//look and i census[(e_ij+e_ik==0)?TRIAD_111U:TRIAD_111D]++; } else // I must decide between 030T and 030C // 030C is all <1,1>, while 030T has a <0,2> and a <2, 0> // so it;s enough to look at atmost 2 nodes for <1,1> census[(e_ij+e_ik==1 && e_ji+e_ki==1 && e_ji+e_jk==1 && e_ij+e_kj==1)? TRIAD_030C: TRIAD_030T]++; break; case 4: if(md==2) census[TRIAD_201]++; else { //120: D, C or U? // <1,1> => C // <2,0> => D // <0,2> => U // they all got <1,2> and <2,1> int in, out; out = e_ij+e_ik; //in of i. in = e_ji+e_ki; //out of i if(in+out==2) {//i is the node switch(out) { case 0: census[TRIAD_120U]++; break; case 1: census[TRIAD_120C]++; break; case 2: census[TRIAD_120D]++; break; } break; } in = e_ij+e_kj; //in of j. out = e_ji+e_jk; //out of j if(in+out==2) {//j is the node switch(out) { case 0: census[TRIAD_120U]++; break; case 1: census[TRIAD_120C]++; break; case 2: census[TRIAD_120D]++; break; } break; } in = e_ik+e_jk; //in of k. out = e_ki+e_kj; //out of k if(in+out==2) {//k is the node switch(out) { case 0: census[TRIAD_120U]++; break; case 1: census[TRIAD_120C]++; break; case 2: census[TRIAD_120D]++; break; } break; } } break; case 5: census[TRIAD_210]++;break; case 6: census[TRIAD_300]++;break; } } } } return census; } /** * Returns the number of direct triads * (number of parent nodes that point to both nodes sent as parameters). * @author Liviu Panait */ public static int getNumberDirectTriads( final Network network, final Object node1, final Object node2 ) { if( !network.isDirected() ) throw new RuntimeException( "NetworkStatistics.getNumberDirectTriads should be called only with directed graphs" ); int result = 0; HashSet hash = new HashSet(); final Bag out1 = network.getEdgesIn(node1); for( int i = 0 ; i < out1.numObjs ; i++ ) hash.add( ((Edge)(out1.objs[i])).from() ); final Bag out2 = network.getEdgesIn(node2); for( int i = 0 ; i < out2.numObjs ; i++ ) if( hash.contains( ((Edge)(out2.objs[i])).from() ) ) result++; return result; } public static final int DYAD_MUTUAL = 0; public static final int DYAD_ASYMMETRIC = 1; public static final int DYAD_NULL = 2; public static final String[] DYAD_CLASSES = { "M", "A", "N" }; /** * Computes thw dyad census (Wasserman and Faust, pages 512). * It assumes a single asymmetric relation where weights are irrelevant. * O(n^2) space and time. * I could do O(1) space and n x outdegree^2 time, but that's O(n^3) worst time * @return 3-length int array */ public static int[] dyadCensus( final Network network) { int[] census = new int[3]; int n = network.allNodes.numObjs; Edge[][] adjacencyMatrix = network.getAdjacencyMatrix(); for(int i=1;i<n;i++) { Edge[] adj_i = adjacencyMatrix[i]; for(int j=0;j<i;j++) { int k=(adjacencyMatrix[j][i]==null? 0:1)+(adj_i[j]==null?0:1); census[2-k]++; } } return census; } }