BenjaminLind

igraphHomophily.R

Jul 13th, 2014
578
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
R 62.54 KB | None | 0 0
  1. #####
  2. ### Except where otherwise indicated, the code below is written by Benjamin Lind under CC: BY-NC-SA 3.0
  3. ### If you use or develop this code, please provide proper attribution.
  4. ### You may contact me at lind.benjamin//at//gmail//d0t//com
  5. ### You may also find me @benjaminlind on Twitter
  6. #####
  7.  
  8. ### To load these functions into R, enter:
  9. ###     source("http://pastebin.com/raw.php?i=s2ZcqxSm")
  10.  
  11. ### Functions included in this file:
  12. ###     Krackhardt's E-I index
  13. ###     Graph generation based on homophily
  14. ###         Homophily graphs can be created according to general tie probabilities within and between groups.
  15. ###         Homophily graphs can also created according to tie probabilities within and between specific groups.
  16. ###         These functions can be used for any categorical attribute, including blocks or communities.
  17. ###     Tie probability calculators within and between groups.
  18. ###         The values produced from these functions can be used in the homophily graph generators.
  19. ###     Gould and Fernandez's (1989) brokerage measurements
  20. ###         Offers corrections to brokerage() on undirected graphs in library(sna)
  21. ###         Includes an attribute permutation test
  22. ###     Additional functions not related to homophily, but otherwise useful and not in igraph
  23. ###         Weak transitivity as measured for a directed network
  24. ###         UMAN random graph generators. They can make random graphs conditioned on the dyad census.
  25. ###         A dyad rewired graph. Like igraph::rewire(), but controls for reciprocity.
  26. ###         Vazquez random graph generator.
  27. ###         Krackhardt's (1994) measurements of hierarchical organizations. These include:
  28. ###             Hierarchy
  29. ###             Efficiency
  30. ###             Connectedness
  31. ###             LUBness.
  32. ###                 The code for LUBness below was initially written by Butts (2014) for library(sna).
  33. ###                 I have adapted the code here for igraph and Rcpp.
  34.  
  35. library(Rcpp)
  36. library(igraph)
  37. library(compiler)
  38.  
  39. cppFunction('
  40.    double ei_index_cpp(IntegerMatrix el, IntegerVector attrib){
  41.        double incount = 0;
  42.        int n = el.nrow();
  43.        for (int i = 0; i < n; ++i){
  44.            int attrib1 = attrib[el(i, 0) - 1];
  45.            int attrib2 = attrib[el(i, 1) - 1];
  46.            if (attrib1 == attrib2){
  47.                incount += 1;
  48.                }
  49.            }
  50.        double excount = n - incount;
  51.        double out = (excount - incount) / n;
  52.        return out;
  53.        }
  54. ')
  55.  
  56. cppFunction('
  57.    LogicalMatrix homophily_game_cpp_general(IntegerVector m, NumericVector p, bool dir, bool loop){
  58.        int n = m.size();
  59.        LogicalMatrix out(n, n);
  60.        bool samegroup = false;
  61.        double tp = 0.0;
  62.        int addedgeint = 0;
  63.        double addedgedoub = 0.0;
  64.        bool addedge = false;
  65.        
  66.        if (dir & (loop == false)){
  67.            for (int i = 0; i < n; ++i){
  68.                for (int j = 0; j < n; ++j){
  69.                    if (i != j){
  70.                        samegroup = (m[i] == m[j]);
  71.                        if (samegroup){
  72.                            tp = p[0];                        
  73.                            }
  74.                        else{
  75.                            tp = p[1];
  76.                            }
  77.                        addedgeint = rand() % 10000000 + 1;
  78.                        addedgedoub = addedgeint / 10000000.0;
  79.                        addedge = addedgedoub < tp;
  80.                        if (addedge){
  81.                            out(i, j) = true;
  82.                            }
  83.                        else{
  84.                            out(i, j) = false;
  85.                            }
  86.                        }
  87.                    else{
  88.                        out(i, j) = false;
  89.                        }
  90.                    }
  91.                }            
  92.            }
  93.        else if (dir & loop){
  94.            for (int i = 0; i < n; ++i){
  95.                for (int j = 0; j < n; ++j){
  96.                    samegroup = (m[i] == m[j]);
  97.                    if (samegroup){
  98.                        tp = p[0];                        
  99.                        }
  100.                    else{
  101.                        tp = p[1];
  102.                        }
  103.                    addedgeint = rand() % 10000000 + 1;
  104.                    addedgedoub = addedgeint / 10000000.0;
  105.                    addedge = addedgedoub < tp;
  106.                    if (addedge){
  107.                        out(i, j) = true;
  108.                        }
  109.                    else{
  110.                        out(i, j) = false;
  111.                        }
  112.                    }
  113.                }            
  114.            }
  115.        else if ((dir == false) & (loop == false)){
  116.            for (int i = 0; i < n; ++i){
  117.                for (int j = i; j < n; ++j){
  118.                    if (i != j){
  119.                        samegroup = (m[i] == m[j]);
  120.                        if (samegroup){
  121.                            tp = p[0];                        
  122.                            }
  123.                        else{
  124.                            tp = p[1];
  125.                            }
  126.                        addedgeint = rand() % 10000000 + 1;
  127.                        addedgedoub = addedgeint / 10000000.0;
  128.                        addedge = addedgedoub < tp;
  129.                        if (addedge){
  130.                            out(i, j) = true;
  131.                            out(j, i) = true;
  132.                            }
  133.                        else{
  134.                            out(i, j) = false;
  135.                            out(j, i) = false;
  136.                            }
  137.                        }
  138.                    else{
  139.                        out(i, j) = false;
  140.                        }
  141.                    }
  142.                }            
  143.            }
  144.        else{
  145.            for (int i = 0; i < n; ++i){
  146.                for (int j = i; j < n; ++j){
  147.                    samegroup = (m[i] == m[j]);
  148.                    if (samegroup){
  149.                        tp = p[0];                        
  150.                        }
  151.                    else{
  152.                        tp = p[1];
  153.                        }
  154.                    addedgeint = rand() % 10000000 + 1;
  155.                    addedgedoub = addedgeint / 10000000.0;
  156.                    addedge = addedgedoub < tp;
  157.                    if (addedge){
  158.                        out(i, j) = true;
  159.                        out(j, i) = true;
  160.                        }
  161.                    else{
  162.                        out(i, j) = false;
  163.                        out(j, i) = false;
  164.                        }
  165.                    }
  166.                }            
  167.            }
  168.        return out;
  169.        }
  170. ')
  171.  
  172. # To make a faster version of the next function, use the probabilities to estimate the number of edges in each pairing.
  173. # Then, for each pairing, randomly select a pair, verify don't already exist in the edge list, then add to the edge list until the quota is met.
  174. # (A similar method could be adopted that integrates dyads into each group pairing.)
  175.  
  176. cppFunction('
  177.    LogicalMatrix homophily_game_cpp_mixed(IntegerVector m, NumericMatrix p, bool dir, bool loop){
  178.        int n = m.size();
  179.        LogicalMatrix out(n, n);
  180.        double tp = 0.0;
  181.        int addedgeint = 0;
  182.        double addedgedoub = 0.0;
  183.        bool addedge = false;
  184.        
  185.        if (dir & (loop == false)){
  186.            for (int i = 0; i < n; ++i){
  187.                for (int j = 0; j < n; ++j){
  188.                    if (i != j){
  189.                        tp = p(m[i] - 1, m[j] - 1);
  190.                        addedgeint = rand() % 10000000 + 1;
  191.                        addedgedoub = addedgeint / 10000000.0;
  192.                        addedge = addedgedoub < tp;
  193.                        if (addedge){
  194.                            out(i, j) = true;
  195.                            }
  196.                        else{
  197.                            out(i, j) = false;
  198.                            }
  199.                        }
  200.                    else{
  201.                        out(i, j) = false;
  202.                        }
  203.                    }
  204.                }            
  205.            }
  206.        else if (dir & loop){
  207.            for (int i = 0; i < n; ++i){
  208.                for (int j = 0; j < n; ++j){
  209.                    tp = p(m[i] - 1, m[j] - 1);
  210.                    addedgeint = rand() % 10000000 + 1;
  211.                    addedgedoub = addedgeint / 10000000.0;
  212.                    addedge = addedgedoub < tp;
  213.                    if (addedge){
  214.                        out(i, j) = true;
  215.                        }
  216.                    else{
  217.                        out(i, j) = false;
  218.                        }
  219.                    }
  220.                }            
  221.            }
  222.        else if ((dir == false) & (loop == false)){
  223.            for (int i = 0; i < n; ++i){
  224.                for (int j = i; j < n; ++j){
  225.                    if (i != j){
  226.                        tp = p(m[i] - 1, m[j] - 1);
  227.                        addedgeint = rand() % 10000000 + 1;
  228.                        addedgedoub = addedgeint / 10000000.0;
  229.                        addedge = addedgedoub < tp;
  230.                        if (addedge){
  231.                            out(i, j) = true;
  232.                            out(j, i) = true;
  233.                            }
  234.                        else{
  235.                            out(i, j) = false;
  236.                            out(j, i) = false;
  237.                            }
  238.                        }
  239.                    else{
  240.                        out(i, j) = false;
  241.                        }
  242.                    }
  243.                }            
  244.            }
  245.        else{
  246.            for (int i = 0; i < n; ++i){
  247.                for (int j = i; j < n; ++j){
  248.                    tp = p(m[i] - 1, m[j] - 1);
  249.                    addedgeint = rand() % 10000000 + 1;
  250.                    addedgedoub = addedgeint / 10000000.0;
  251.                    addedge = addedgedoub < tp;
  252.                    if (addedge){
  253.                        out(i, j) = true;
  254.                        out(j, i) = true;
  255.                        }
  256.                    else{
  257.                        out(i, j) = false;
  258.                        out(j, i) = false;
  259.                        }
  260.                    }
  261.                }            
  262.            }
  263.        return out;
  264.        }
  265. ')
  266.  
  267. cppFunction('
  268.    NumericVector group_density_match_cpp(IntegerMatrix el, IntegerVector attrib, bool dir, bool loop){
  269.        double incount = 0;
  270.        int n = el.nrow();
  271.        int attrib1 = 0;
  272.        int attrib2 = 0;
  273.        
  274.        for (int i = 0; i < n; ++i){
  275.            attrib1 = attrib[el(i, 0) - 1];
  276.            attrib2 = attrib[el(i, 1) - 1];
  277.            if (attrib1 == attrib2){
  278.                incount += 1;
  279.                }
  280.            }
  281.        double excount = n - incount;
  282.        n = attrib.size();
  283.        double nposse = 0;
  284.        if (dir & loop){
  285.            nposse = pow(n, 2);
  286.            }
  287.        else if (dir & (loop == false)){
  288.            nposse = n * (n - 1);
  289.            }
  290.        else if ((dir == false) & loop){
  291.            nposse = ((n * (n - 1)) / 2) + n;
  292.            }
  293.        else{
  294.            nposse = (n * (n - 1)) / 2;
  295.            }            
  296.        
  297.        int mmax = 0;        
  298.        for (int i = 0; i < n; ++i){
  299.            if (attrib[i] > mmax){
  300.                mmax = attrib[i];
  301.                }
  302.            }
  303.  
  304.        IntegerVector gsize(mmax);
  305.        for (int i = 0; i < mmax; ++i){
  306.            gsize[i] = 0;
  307.            }
  308.        for (int i = 0; i < n; ++i){
  309.            gsize[attrib[i] - 1] += 1;
  310.            }
  311.            
  312.        double npossein = 0.0;        
  313.        if (dir & loop){
  314.            for (int i = 0; i < mmax; ++i){
  315.                npossein += pow(gsize[i], 2);
  316.                }
  317.            }
  318.        else if (dir & (loop == false)){
  319.            for (int i = 0; i < mmax; ++i){
  320.                npossein += gsize[i] * (gsize[i] - 1);
  321.                }
  322.            }
  323.        else if ((dir == false) & loop){
  324.            for (int i = 0; i < mmax; ++i){
  325.                npossein += ((gsize[i] * (gsize[i] - 1)) / 2) + gsize[i];
  326.                }
  327.            }
  328.        else{
  329.            for (int i = 0; i < mmax; ++i){
  330.                npossein += (gsize[i] * (gsize[i] - 1)) / 2;
  331.                }
  332.            }
  333.        
  334.        NumericVector out(2);
  335.        out[0] = incount / npossein;
  336.        out[1] = excount / (nposse - npossein);
  337.        out.names() = CharacterVector::create("internal", "external");
  338.        return out;
  339.        }
  340. ')
  341.  
  342. cppFunction('
  343.    NumericMatrix group_density_mixture_cpp(IntegerMatrix el, IntegerVector attrib, bool dir, bool loop){
  344.        int mmax = 0;
  345.        int n = attrib.size();
  346.        
  347.        for (int i = 0; i < n; ++i){
  348.            if (attrib[i] > mmax){
  349.                mmax = attrib[i];
  350.                }
  351.            }
  352.        
  353.        NumericMatrix ecounts(mmax, mmax);
  354.        int attrib1 = 0;
  355.        int attrib2 = 0;        
  356.        int ne = el.nrow();
  357.        
  358.        for (int i = 0; i < mmax; ++i){
  359.            for (int j = i; j < mmax; ++j){
  360.                ecounts(i, j) = 0.0;
  361.                ecounts(j, i) = 0.0;
  362.                }
  363.            }
  364.  
  365.        if (dir){
  366.            for (int i = 0; i < ne; ++i){
  367.                attrib1 = attrib[el(i, 0) - 1] - 1;
  368.                attrib2 = attrib[el(i, 1) - 1] - 1;
  369.                ecounts(attrib1, attrib2) += 1.0;
  370.                }
  371.            }
  372.        else{
  373.            for (int i = 0; i < ne; ++i){
  374.                attrib1 = attrib[el(i, 0) - 1] - 1;
  375.                attrib2 = attrib[el(i, 1) - 1] - 1;
  376.                ecounts(attrib1, attrib2) += 1;
  377.                if (attrib1 != attrib2){
  378.                    ecounts(attrib2, attrib1) += 1.0;
  379.                    }
  380.                }
  381.            }
  382.  
  383.        NumericVector gsize(mmax);
  384.        for (int i = 0; i < mmax; ++i){
  385.            gsize[i] = 0.0;
  386.            }
  387.        for (int i = 0; i < n; ++i){
  388.            gsize[attrib[i] - 1] += 1;
  389.            }
  390.        if (dir & loop){        
  391.            for (int i = 0; i < mmax; ++i){
  392.                for (int j = i; j < mmax; ++j){
  393.                    if (i != j){
  394.                        ecounts(i, j) = ecounts(i, j) / (gsize[i] * gsize[j]);
  395.                        ecounts(j, i) = ecounts(j, i) / (gsize[j] * gsize[i]);
  396.                        }
  397.                    else{
  398.                        ecounts(i, j) = ecounts(i, j) / pow(gsize[i], 2);
  399.                        }
  400.                    }
  401.                }
  402.            }
  403.        else if (dir & (loop == false)){
  404.            for (int i = 0; i < mmax; ++i){
  405.                for (int j = i; j < mmax; ++j){
  406.                    if (i != j){
  407.                        ecounts(i, j) = ecounts(i, j) / (gsize[i] * gsize[j]);
  408.                        ecounts(j, i) = ecounts(j, i) / (gsize[j] * gsize[i]);
  409.                        }
  410.                    else{
  411.                        ecounts(i, j) = ecounts(i, j) / (gsize[i] * (gsize[i] - 1));
  412.                        }
  413.                    }
  414.                }
  415.            }
  416.        else if ((dir == false) & loop){
  417.            for (int i = 0; i < mmax; ++i){
  418.                for (int j = i; j < mmax; ++j){
  419.                    if (i != j){
  420.                        ecounts(i, j) = ecounts(i, j) / (gsize[i] * gsize[j]);
  421.                        ecounts(j, i) = ecounts(j, i) / (gsize[j] * gsize[i]);
  422.                        }
  423.                    else{
  424.                        ecounts(i, j) = ecounts(i, j) / (((gsize[i] * (gsize[i] - 1)) / 2) + gsize[i]);
  425.                        }
  426.                    }
  427.                }
  428.            }
  429.        else{
  430.            for (int i = 0; i < mmax; ++i){
  431.                for (int j = i; j < mmax; ++j){
  432.                    if (i != j){
  433.                        ecounts(i, j) = ecounts(i, j) / (gsize[i] * gsize[j]);
  434.                        ecounts(j, i) = ecounts(j, i) / (gsize[j] * gsize[i]);
  435.                        }
  436.                    else{
  437.                        ecounts(i, j) = ecounts(i, j) / ((gsize[i] * (gsize[i] - 1)) / 2);
  438.                        }
  439.                    }
  440.                }
  441.            }
  442.            
  443.        return ecounts;
  444.        }
  445. ')
  446.  
  447. cppFunction('
  448.    IntegerMatrix replace_multiple_ties_cpp(IntegerMatrix el, IntegerVector sinds, IntegerVector rinds, bool dir, bool loop){
  449.        int nrow = el.nrow();
  450.        int ncol = el.ncol();
  451.        IntegerMatrix out(nrow, ncol);
  452.        out(0, 0) = el(0, 0);
  453.        out(0, 1) = el(0, 1);
  454.        for (int i = 1; i < nrow; ++i){
  455.            bool reprow = false;
  456.            if ((loop == false) & (el(i, 0) == el(i, 1))){
  457.                reprow = true;
  458.                }
  459.            int j = 0;
  460.            while ((j < i) & (reprow == false)){
  461.                if ((el(i, 0) == out(j, 0)) & (el(i, 1) == out(j, 1))){
  462.                    reprow = true;
  463.                    }
  464.                if ((dir == false) & ((el(i, 0) == out(j, 1)) & (el(i, 1) == out(j, 0)))){
  465.                    reprow = true;
  466.                    }
  467.                j += 1;
  468.                }
  469.            out(i, 0) = el(i, 0);
  470.            out(i, 1) = el(i, 1);
  471.            while (reprow == true){
  472.                out(i, 0) = sinds[rand() % sinds.size()];
  473.                out(i, 1) = rinds[rand() % rinds.size()];
  474.                bool newreprow = false;
  475.                if ((loop == false) & (out(i, 0) == out(i, 1))){
  476.                    newreprow = true;
  477.                    }
  478.                int j = 0;
  479.                while ((j < i) & (newreprow == false)){
  480.                    if ((out(i, 0) == out(j, 0)) & (out(i, 1) == out(j, 1))){
  481.                        newreprow = true;
  482.                        }
  483.                    if ((dir == false) & ((out(i, 0) == out(j, 0)) & (out(i, 1) == out(j, 1)))){
  484.                        newreprow = true;
  485.                        }
  486.                    j += 1;
  487.                    }
  488.                reprow = newreprow;
  489.                }
  490.            }
  491.        return(out);
  492.        }
  493. ')
  494.  
  495. homophily_cpp <- list(ei = ei_index_cpp, group_density = list(general = group_density_match_cpp, mixed = group_density_mixture_cpp), games = list(general = homophily_game_cpp_general, mixed = homophily_game_cpp_mixed), assist = list(replace_multiple_ties = replace_multiple_ties_cpp))
  496.  
  497. rm(ei_index_cpp, group_density_match_cpp, group_density_mixture_cpp, homophily_game_cpp_general, homophily_game_cpp_mixed, replace_multiple_ties_cpp)
  498.  
  499. ei_index <- function(g, value){
  500.     # _g_ is an igraph 'graph' object
  501.     # _value_ is a categorical vertex attribute
  502.     if (any(is.na(value)))
  503.         stop("Missing values are not allowed for _value_.")
  504.     if (vcount(g) != length(value))
  505.         stop("The length of _value_ does not equal the number of vertices in _g_.")
  506.     g.el <- get.edgelist(g, names = FALSE)
  507.     allvalues <- unique(value)
  508.     value.int <- match(value, allvalues)
  509.     return(homophily_cpp$ei(g.el, value.int))
  510.     }
  511. ei_index <- cmpfun(ei_index)
  512.  
  513. homophily_game <- function(groups.n, tie.prob, directed = TRUE, loops = FALSE, sparse = FALSE){
  514.     # _groups.n_ is a vector of integers indicating the size of each group.
  515.     # _tie.prob_ needs to be either a matrix or a vector of length two.
  516.         # If a vector
  517.             # The first value needs to refer to the tie probability within groups
  518.             # The second value needs to refer to the tie probability between groups
  519.         # If a matrix
  520.             # Values are proportional to the tie probability within and between groups.
  521.             # It must be square
  522.         # Note that the tie probability is for the pairings. The denominator varies depending on group size.
  523.     # _directed_ indicates whether or not the simulated network should be directed.
  524.     # _loops_ indicates whether or not loops should be allowed.
  525.     # Set _sparse_ equal to TRUE for large (> ~1000 vertices) and sparse networks ( < ~0.01 density).
  526.         # The default argument (FALSE) create matrices that occupy considerable memory for large networks.
  527.             # It works great for small networks, as indexing is very fast.
  528.         # The sparse algorithm involves creating an edge list with randomly selected nodes.
  529.             # This technique introduces the possibility for multiple edges to occur.
  530.             # To correct the possibility of multiple edges, the algorithm loops through the edge list to replace them.
  531.  
  532.     groups.n <- round(groups.n)
  533.     s <- length(groups.n)
  534.     if (s < 1)
  535.         stop("There must be at least two groups indicated by _groups.n_.")
  536.     if (any(tie.prob < 0) | any(tie.prob > 1))
  537.         stop("_tie.prob_ must have all values be greater than zero and less than one to indicate the desnity within and between groups.")
  538.     if (any(is.na(tie.prob)))
  539.         stop("Missing data in _tie.prob_ is not supported.")
  540.     if ((length(tie.prob) != 2) & (length(tie.prob) != s^2))
  541.         stop("_tie.prob_ needs either two values or a square density matrix.")
  542.     if (is.logical(directed) == FALSE)
  543.         stop("_directed_ must be a logical argument to indicate directionality of the desired graph.")
  544.  
  545.     n <- sum(groups.n)
  546.     group.assignments <- unlist(sapply(1:s, function(x) rep(x, times = groups.n[x])))
  547.     if (length(tie.prob) > 2){
  548.         if (nrow(tie.prob) != ncol(tie.prob))
  549.             stop("Number of columns does not equal the number of rows provided in _tie.prob_")
  550.         if (nrow(tie.prob) != s)
  551.             stop("Number of rows and columns does not equal the number of groups provided in _tie.prob_.")
  552.         if (sparse){
  553.             add.inds <- c(0, cumsum(groups.n[1:(s-1)]))
  554.             intergroup.graphs <- lapply(1:s, function(x){
  555.                 newg <- erdos.renyi.game(groups.n[x], tie.prob[x, x], type = "gnp", directed = directed, loops = loops)
  556.                 newg <- get.edgelist(newg, names = FALSE)
  557.                 newg <- as.matrix(newg) + add.inds[x]
  558.                 return(newg)
  559.                 }
  560.                 )
  561.             intergroup.graphs <- do.call(rbind, intergroup.graphs)
  562.             if (directed){
  563.                 for (i in 1:s){
  564.                     for (j in 1:s){
  565.                         if (i != j){
  566.                             expties <- round(groups.n[i] * groups.n[j] * tie.prob[i, j])
  567.                             if (expties > 0){
  568.                                 send <- add.inds[i] + sample.int(n = groups.n[i], size = expties, replace = TRUE)
  569.                                 rec <- add.inds[j] + sample.int(n = groups.n[j], size = expties, replace = TRUE)
  570.                                 if (expties == 1)
  571.                                     intergroup.graphs <- rbind(intergroup.graphs, c(send, rec))
  572.                                 else{
  573.                                     newg <- matrix(NA, nrow = expties, ncol = 2)
  574.                                     newg[, 1] <- send
  575.                                     newg[, 2] <- rec
  576.                                     possible.mult.ties <- duplicated(send) * duplicated(rec)
  577.                                     if (any(possible.mult.ties)){
  578.                                         iinds <- add.inds[i] + 1:groups.n[i]
  579.                                         jinds <- add.inds[j] + 1:groups.n[j]
  580.                                         newg <- homophily_cpp$assist$replace_multiple_ties(newg, iinds, jinds, directed, loops)
  581.                                         intergroup.graphs <- rbind(intergroup.graphs, newg)
  582.                                         }
  583.                                     else
  584.                                        intergroup.graphs <- rbind(intergroup.graphs, newg)
  585.                                     }
  586.                                 }
  587.                             }
  588.                         }
  589.                     }
  590.                 }
  591.             else{
  592.                 for (i in 1:(s - 1)){
  593.                     for (j in (i + 1):s){
  594.                         expties <- round(groups.n[i] * groups.n[j] * tie.prob[i, j])
  595.                         if (expties > 0){
  596.                             send <- add.inds[i] + sample.int(n = groups.n[i], size = expties, replace = TRUE)
  597.                             rec <- add.inds[j] + sample.int(n = groups.n[j], size = expties, replace = TRUE)
  598.                             if (expties == 1)
  599.                                 intergroup.graphs <- rbind(intergroup.graphs, c(send, rec))
  600.                             else{
  601.                                 newg <- matrix(nrow = expties, ncol = 2)
  602.                                 newg[, 1] <- send
  603.                                 newg[, 2] <- rec
  604.                                 possible.mult.ties <- duplicated(send) * duplicated(rec)
  605.                                 if (any(possible.mult.ties)){
  606.                                     iinds <- add.inds[i] + 1:groups.n[i]
  607.                                     jinds <- add.inds[j] + 1:groups.n[j]
  608.                                     newg <- homophily_cpp$assist$replace_multiple_ties(newg, iinds, jinds, directed, loops)
  609.                                     intergroup.graphs <- rbind(intergroup.graphs, newg)
  610.                                     }
  611.                                 else
  612.                                     intergroup.graphs <- rbind(intergroup.graphs, newg)
  613.                                 }
  614.                             }
  615.                         }
  616.                     }
  617.                 }
  618.             g <- as.data.frame(intergroup.graphs)
  619.             g <- graph.data.frame(g, directed = directed, vertices = data.frame(name = 1:n, group = group.assignments))
  620.             }
  621.         else{
  622.             g <- homophily_cpp$games$mixed(group.assignments, tie.prob, directed, loops)
  623.             g <- graph.adjacency(g, mode = ifelse(directed, "directed", "undirected"), diag = loops)
  624.             }
  625.         }
  626.     else{
  627.         if (sparse){
  628.             add.inds <- c(0, cumsum(groups.n[1:(s-1)]))
  629.             intergroup.graphs <- lapply(1:s, function(x){
  630.                 newg <- erdos.renyi.game(groups.n[x], tie.prob[1], type = "gnp", directed = directed, loops = loops)
  631.                 newg <- get.edgelist(newg, names = FALSE)
  632.                 newg <- as.matrix(newg) + add.inds[x]
  633.                 return(newg)
  634.                 }
  635.                 )
  636.             intergroup.graphs <- do.call(rbind, intergroup.graphs)
  637.             if (directed){
  638.                 for (i in 1:s){
  639.                     for (j in 1:s){
  640.                         if (i != j){
  641.                             expties <- round(groups.n[i] * groups.n[j] * tie.prob[2])
  642.                             if (expties > 0){
  643.                                 send <- add.inds[i] + sample.int(n = groups.n[i], size = expties, replace = TRUE)
  644.                                 rec <- add.inds[j] + sample.int(n = groups.n[j], size = expties, replace = TRUE)
  645.                                 if (expties == 1)
  646.                                     intergroup.graphs <- rbind(intergroup.graphs, c(send, rec))
  647.                                 else{
  648.                                     newg <- matrix(NA, nrow = expties, ncol = 2)
  649.                                     newg[, 1] <- send
  650.                                     newg[, 2] <- rec
  651.                                     possible.mult.ties <- duplicated(send) * duplicated(rec)
  652.                                     if (any(possible.mult.ties)){
  653.                                         iinds <- add.inds[i] + 1:groups.n[i]
  654.                                         jinds <- add.inds[j] + 1:groups.n[j]
  655.                                         newg <- homophily_cpp$assist$replace_multiple_ties(newg, iinds, jinds, directed, loops)
  656.                                         intergroup.graphs <- rbind(intergroup.graphs, newg)
  657.                                         }
  658.                                     else
  659.                                        intergroup.graphs <- rbind(intergroup.graphs, newg)
  660.                                     }
  661.                                 }
  662.                             }
  663.                         }
  664.                     }
  665.                 }
  666.             else{
  667.                 for (i in 1:(s - 1)){
  668.                     for (j in (i + 1):s){
  669.                         expties <- round(groups.n[i] * groups.n[j] * tie.prob[2])
  670.                         if (expties > 0){
  671.                             send <- add.inds[i] + sample.int(n = groups.n[i], size = expties, replace = TRUE)
  672.                             rec <- add.inds[j] + sample.int(n = groups.n[j], size = expties, replace = TRUE)
  673.                             if (expties == 1)
  674.                                 intergroup.graphs <- rbind(intergroup.graphs, c(send, rec))
  675.                             else{
  676.                                 newg <- matrix(nrow = expties, ncol = 2)
  677.                                 newg[, 1] <- send
  678.                                 newg[, 2] <- rec
  679.                                 possible.mult.ties <- duplicated(send) * duplicated(rec)
  680.                                 if (any(possible.mult.ties)){
  681.                                     iinds <- add.inds[i] + 1:groups.n[i]
  682.                                     jinds <- add.inds[j] + 1:groups.n[j]
  683.                                     newg <- homophily_cpp$assist$replace_multiple_ties(newg, iinds, jinds, directed, loops)
  684.                                     intergroup.graphs <- rbind(intergroup.graphs, newg)
  685.                                     }
  686.                                 else
  687.                                     intergroup.graphs <- rbind(intergroup.graphs, newg)
  688.                                 }
  689.                             }
  690.                         }
  691.                     }
  692.                 }
  693.             g <- as.data.frame(intergroup.graphs)
  694.             g <- graph.data.frame(g, directed = directed, vertices = data.frame(name = 1:n, group = group.assignments))
  695.             }
  696.         else{
  697.             g <- homophily_cpp$games$general(group.assignments, tie.prob, directed, loops)
  698.             g <- graph.adjacency(g, mode = ifelse(directed, "directed", "undirected"), diag = loops)
  699.             }
  700.         }
  701.     V(g)$name <- 1:n
  702.     V(g)$group <- group.assignments
  703.     return(g)
  704.     }
  705. homophily_game <- cmpfun(homophily_game)
  706.  
  707. group_tie_prob <- function(g, m, mode = "general"){
  708.     # _g_ must be an igraph graph object
  709.     # _m_ must be a vector indicating group membership
  710.     # _mode_ indicates the type of output.
  711.         # "general" returns a vector with two values, one for the density within all groups and the other for between them.
  712.         # "mixture" returns a matrix with densities within and between each group
  713.     if (mode %in% c("general", "mixture") == FALSE)
  714.         stop("_mode_ must be either 'general' or 'mixture'.")
  715.     if (length(m) != vcount(g))
  716.         stop("Length of _m_ does not equal the number of vertices in _g_.")
  717.     g.el <- get.edgelist(g, names = FALSE)
  718.     all.m <- unique(m)
  719.     new.m <- match(m, all.m)
  720.     if (mode == "general")
  721.         out <- homophily_cpp$group_density$general(g.el, new.m, is.directed(g), any(is.loop(g)))
  722.     else{
  723.         out <- homophily_cpp$group_density$mixed(g.el, new.m, is.directed(g), any(is.loop(g)))
  724.         rownames(out) <- as.character(all.m)
  725.         colnames(out) <- as.character(all.m)
  726.         }
  727.     return(out)
  728.     }
  729. group_tie_prob <- cmpfun(group_tie_prob)
  730.  
  731. ###
  732. # In writing the function below, brokerage_roles(), I have found two errors with the comparable function, brokerage(), in library(sna).
  733. # First, brokerage() does not account for partial brokering. See the first paragraph on page 98 of of Gould and Fernandez (1989). brokerage_roles() allows for either implementation.
  734. # Second, for undirected networks, brokerage() uses some double counting.
  735. #     Coordinator, Itinerant broker, and Liaison roles are counted twice.
  736. #     Gatekeeper and Representative seem to be counted correctly.
  737. #     Total brokerage includes the double counted along with adding both Gatekeeper and Representative.
  738. #         (Gatekeeper and Representative are the same and should only count once in the total.)
  739. #     brokerage() is theoretically correct if we think of brokerage as a process of mutual flow in undirected graphs, counting the effect for each brokered vertex.
  740. #     brokerage() is theoretically incorrect if we think of brokerage as a path-based measurement in undirected graphs.
  741. #     Given the emphasis on paths in Gould and Fernandez (1989:95-96), I am inclined to think it's incorrect.
  742. #     (Admittedly, G&F only spoke of directed graphs.)
  743. ###
  744.  
  745. # Returns Coordinator, Itinerant broker, Gatekeeper, Representative, Liaison, and Total.
  746. cppFunction('
  747.    NumericMatrix brokerage_counts(IntegerMatrix twopaths, NumericVector weights, IntegerVector m, bool dir){
  748.        int n = m.size();
  749.        int b;
  750.        int satt, batt, ratt;
  751.        NumericMatrix out(n, 6);
  752.        for (int i = 0; i < n; ++i){
  753.            for (int j = 0; j < 6; ++j){
  754.                out(i, j) = 0.0;
  755.                }
  756.            }
  757.        if (dir){
  758.            for (int i = 0; i < twopaths.nrow(); ++i){
  759.                b = twopaths(i, 1) - 1;
  760.                satt = m[twopaths(i, 0) - 1];
  761.                batt = m[twopaths(i, 1) - 1];
  762.                ratt = m[twopaths(i, 2) - 1];
  763.                if ((satt == batt) & (batt == ratt) & (satt == ratt)){
  764.                    out(b, 0) += weights[i];
  765.                    }
  766.                else if ((satt != batt) & (batt != ratt) & (satt == ratt)){
  767.                    out(b, 1) += weights[i];
  768.                    }
  769.                else if ((satt != batt) & (batt != ratt) & (satt != ratt)){
  770.                    out(b, 4) += weights[i];
  771.                    }
  772.                else if ((satt != batt) & (batt == ratt) & (satt != ratt)){
  773.                    out(b, 2) += weights[i];
  774.                    }
  775.                else{
  776.                    out(b, 3) += weights[i];
  777.                    }
  778.                out(b, 5) += weights[i];
  779.                }
  780.            }
  781.        else{
  782.            for (int i = 0; i < twopaths.nrow(); ++i){
  783.                b = twopaths(i, 1) - 1;
  784.                satt = m[twopaths(i, 0) - 1];
  785.                batt = m[twopaths(i, 1) - 1];
  786.                ratt = m[twopaths(i, 2) - 1];
  787.                if ((satt == batt) & (batt == ratt) & (satt == ratt)){
  788.                    out(b, 0) += weights[i];
  789.                    }
  790.                else if ((satt != batt) & (batt != ratt) & (satt == ratt)){
  791.                    out(b, 1) += weights[i];
  792.                    }
  793.                else if ((satt != batt) & (batt != ratt) & (satt != ratt)){
  794.                    out(b, 4) += weights[i];
  795.                    }
  796.                else{
  797.                    out(b, 2) += weights[i];
  798.                    out(b, 3) += weights[i];
  799.                    }
  800.                out(b, 5) += weights[i];
  801.                }
  802.            }
  803.        return(out);
  804.        }
  805. ')
  806.  
  807. cppFunction('
  808.    IntegerVector group_counts(IntegerVector m){
  809.        int mmax = 0;
  810.        for (int i = 0; i < m.size(); ++i){
  811.            if (m[i] > mmax){
  812.                mmax = m[i];
  813.                }
  814.            }
  815.        IntegerVector out(mmax);
  816.        for (int i = 0; i < mmax; ++i){
  817.            out[i] = 0;
  818.            }
  819.        for (int i = 0; i < m.size(); ++i){
  820.            out[m[i] - 1] += 1;
  821.            }
  822.        return(out);
  823.        }
  824. ')
  825.  
  826. cppFunction('
  827.    NumericMatrix sres2mat(List x, bool partial){
  828.        int nsps = x.size();
  829.        NumericMatrix out(nsps, 4);
  830.        double weight = 1.0 / nsps;
  831.        if (partial){
  832.            for (int i = 0; i < nsps; ++i){
  833.                IntegerVector path = as<IntegerVector>(x[i]);
  834.                out(i, 0) = path[0];
  835.                out(i, 1) = path[1];
  836.                out(i, 2) = path[2];
  837.                out(i, 3) = weight;
  838.                }
  839.            }
  840.        else{
  841.            for (int i = 0; i < nsps; ++i){
  842.                IntegerVector path = as<IntegerVector>(x[i]);
  843.                out(i, 0) = path[0];
  844.                out(i, 1) = path[1];
  845.                out(i, 2) = path[2];
  846.                out(i, 3) = 1.0;
  847.                }
  848.            }
  849.        return(out);
  850.        }
  851. ')
  852.  
  853. brokerage_cpp <- list(brokerage_counts = brokerage_counts, group_counts = group_counts, sp2mat = sres2mat)
  854. rm(brokerage_counts, group_counts, sres2mat)
  855.  
  856. brokerage_roles <- function(g, m, partial = TRUE, statistics = FALSE, iter = 1000){
  857.     # _g_ must be an igraph graph object
  858.     # _m_ must be a vector indicating group membership
  859.     # _partial_ indicates whether brokerage roles can be shared. (See above comment)
  860.     # If TRUE, _statistics_ performs an attribute permutation test of significance
  861.     # _iter_ are the number of iterations to perform in the permutation tests
  862.     mint <- sort(unique(m))
  863.     mint <- match(m, mint) # m as an integer
  864.     directed <- is.directed(g)
  865.     weightsasone <- rep(1, times = ecount(g)) # Remove edge weights if they exist
  866.     sp <- shortest.paths(g, weights = weightsasone, mode = "out")
  867.     twopathsinds <- which(sp == 2, arr.ind = TRUE)
  868.     twopaths <- lapply(1:nrow(twopathsinds), function(x){
  869.              y <- get.all.shortest.paths(g, twopathsinds[x, 1], twopathsinds[x, 2], mode = "out", weights = weightsasone)$res
  870.              y <- brokerage_cpp$sp2mat(y, partial)
  871.              return(y)
  872.             }
  873.         )
  874.     twopaths <- do.call(rbind, twopaths)
  875.     colnames(twopaths) <- c("sender", "broker", "receiver", "weight")
  876.     if (directed == FALSE){ # Remove duplicate paths
  877.         twopaths <- twopaths[which(twopaths[, "sender"] < twopaths[, "receiver"]), ]
  878.         }
  879.  
  880.     raw.nli <- brokerage_cpp$brokerage_counts(twopaths[, c("sender", "broker", "receiver")], twopaths[,c("weight")], mint, directed)
  881.     colnames(raw.nli) <- c("Coordinator", "Itinerant Broker", "Gatekeeper", "Representative", "Liaison", "Total")
  882.  
  883.     raw.gli <- colSums(raw.nli)
  884.  
  885.     N <- brokerage_cpp$group_counts(mint)
  886.     names(N) <- sort(unique(m))
  887.    
  888.     if (statistics){
  889.        sims <- replicate(iter, brokerage_cpp$brokerage_counts(twopaths[, c("sender", "broker", "receiver")], twopaths[,c("weight")], sample(mint), directed))
  890.        exp.nli <- apply(sims, c(1,2), mean)[, -6]
  891.        colnames(exp.nli) <- c("Coordinator", "Itinerant Broker", "Gatekeeper", "Representative", "Liaison")
  892.        sd.nli <- apply(sims, c(1,2), sd)[, -6]
  893.        colnames(sd.nli) <- c("Coordinator", "Itinerant Broker", "Gatekeeper", "Representative", "Liaison")
  894.        z.nli <- (raw.nli[, -6] - exp.nli) / sd.nli
  895.        colnames(z.nli) <- c("Coordinator", "Itinerant Broker", "Gatekeeper", "Representative", "Liaison")
  896.  
  897.        total <- t(apply(sims, 3, colSums))[, -6]
  898.        colnames(total) <- c("Coordinator", "Itinerant Broker", "Gatekeeper", "Representative", "Liaison")
  899.        exp.gli <- colMeans(total)
  900.        sd.gli <- apply(total, 2, sd)
  901.        z.gli <- (raw.gli[-6] - exp.gli) / sd.gli
  902.  
  903.        return(list(nli = list(raw = raw.nli, exp = exp.nli, sd = sd.nli, z = z.nli), gli = list(raw = raw.gli, exp = exp.gli, sd = sd.gli, z = z.gli), group_counts = N))
  904.        }
  905.     else{
  906.         return(list(nli = raw.nli, gli = raw.gli, group_counts = N))
  907.         }
  908.     }
  909. brokerage_roles <- cmpfun(brokerage_roles)
  910.  
  911. transitivity_directed <- function(g, method = "weak"){
  912.     # _g_ is an igraph graph object
  913.     # _method_ is the technique of measuring transitivity.
  914.         # "Weak" is the most common understanding of transitivity, so that's the one used here.
  915.         # "Weak census" returns the counts, not the proportion.
  916.         # Once I find a convenient table showing the weights for vacuous transitive triples and see the formula, I'll include strong transitivity.
  917.  
  918.     if (is.directed(g) == FALSE){
  919.         warning("Entered an undirected graph into transitivity_directed(). Returned transitivity(g).")
  920.         return(transitivity(g))
  921.         }    
  922.     acceptable_methods <- c("weak", "weakcensus")
  923.     if (method %in% acceptable_methods == FALSE)
  924.         stop(paste("_method_ must be in c(\"", paste(acceptable_methods, collapse = "\", \""), "\")", sep = ""))
  925.     # See Wasserman and Faust (1994:573)
  926.     weight_mat <- data.frame(row.names = c("003", "012", "102", "021D", "021U", "021C", "111D", "111U", "030T", "030C", "201", "120D", "120U", "120C", "210", "300"))
  927.     weight_mat$b_in <- c(0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 2, 1, 1, 2, 3)
  928.     weight_mat$b_out <- c(0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 2, 1, 2, 3)
  929.     weight_mat$m <- c(0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 1, 1, 2, 3)
  930.     weight_mat$a <- c(0, 1, 0, 2, 2, 2, 1, 1, 3, 3, 0, 2, 2, 2, 1, 0)
  931.     weight_mat$n <- c(3, 2, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0)
  932.     weight_mat$c <- c(0, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6)
  933.     weight_mat$Trans <- c(0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 1, 3, 6)
  934.     #weight_mat$Vac_trans <- c(6, 6, 6, 6, 6, 5, 5, 5, 5, 3, 4, 4, 4, 3, 2, 0)
  935.     weight_mat$Intrans <- c(0, 0, 0, 0, 0, 1, 1, 1, 0, 3, 2, 0, 0, 2, 1, 0)
  936.     weight_mat$Close_Friends_Disagreeing <- c(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0)
  937.  
  938.     tc <- triad.census(g)
  939.     if (method == "weakcensus"){
  940.         transcounts <- sum(weight_mat$Trans * tc)
  941.         return(transcounts)
  942.         }
  943.     else if (method == "weak"){
  944.         transcounts <- sum(weight_mat$Trans * tc)
  945.         intranscounts <- sum(weight_mat$Intrans * tc)
  946.         return(transcounts / (transcounts + intranscounts))
  947.         }
  948.     }
  949. transitivity_directed <- cmpfun(transitivity_directed)
  950. # Some additional games that are not in igraph.
  951.  
  952. # Find a memory-friendly way to do the next two functions without matrices (rescanning the edges.)
  953. cppFunction('    
  954.    LogicalMatrix uman_game_prob_cpp(int n, NumericVector dc){
  955.        LogicalMatrix out(n, n);
  956.        int adddyadrandint = 0;
  957.        double adddyadranddoub = 0;
  958.        int addmutrandint = 0;
  959.        double addmutranddoub = 0;
  960.        int dironerandint = 0;
  961.        double dironeranddoub = 0;
  962.        bool adddyad = false;
  963.        bool addmut = false;
  964.        bool dirone = false;
  965.        for (int i = 0; i < (n - 1); ++i){
  966.            for (int j = (i + 1); j < n; ++j){
  967.                adddyadrandint = rand() % 10000000 + 1;
  968.                adddyadranddoub = adddyadrandint / 10000000.0;
  969.                adddyad = adddyadranddoub > dc[2];
  970.                if (adddyad){
  971.                    addmutrandint = rand() % 10000000 + 1;
  972.                    addmutranddoub = addmutrandint / 10000000.0;
  973.                    addmut = addmutranddoub <= (dc[0] / (dc[0] + dc[1]));
  974.                    if (addmut){
  975.                        out(i, j) = true;
  976.                        out(j, i) = true;
  977.                        }
  978.                    else{
  979.                        dironerandint = rand() % 10000000 + 1;
  980.                        dironeranddoub = dironerandint / 10000000.0;
  981.                        dirone = dironeranddoub > .5;
  982.                        if (dirone){
  983.                            out(i, j) = true;
  984.                            out(j, i) = false;
  985.                            }
  986.                        else{
  987.                            out(j, i) = true;
  988.                            out(i, j) = false;
  989.                            }
  990.                        }
  991.                    }
  992.                else{
  993.                    out(i, j) = false;
  994.                    out(j, i) = false;                    
  995.                    }
  996.                }
  997.            }
  998.        return out;
  999.        }
  1000. ')
  1001.  
  1002. cppFunction('    
  1003.    IntegerMatrix uman_game_exact_cpp(int n, IntegerVector dc){
  1004.        IntegerMatrix out((dc[0] * 2) + dc[1], 2);
  1005.        LogicalMatrix usededges(n, n);
  1006.        int m = 0;
  1007.        int a = 0;
  1008.        int counter = 0;
  1009.        int r = 0;
  1010.        int c = 0;
  1011.        int addmutint = 0;
  1012.        double addmutdoub = 0.0;
  1013.        bool addmut = false;
  1014.        for (int i = 0; i < n; ++i){
  1015.            for (int j = i; j < n; ++j){
  1016.                usededges(i, j) = false;
  1017.                usededges(j, i) = false;
  1018.                }
  1019.            }
  1020.        while (counter < out.nrow()){
  1021.            r = rand() % n;
  1022.            c = rand() % n;
  1023.            if ((r != c) & (usededges(r, c) == false)){
  1024.                usededges(r, c) = true;
  1025.                usededges(c, r) = true;
  1026.                if ((m < dc[0]) & (a < dc[1])){
  1027.                    addmutint = rand() % 10000000 + 1;
  1028.                    addmutdoub = addmutint / 10000000.0;
  1029.                    addmut = addmutdoub > (dc[1] / (dc[0] + dc[1]));
  1030.                    }
  1031.                else if (m == dc[0]){
  1032.                    addmut = false;
  1033.                    }
  1034.                else{
  1035.                    addmut = true;
  1036.                    }
  1037.                if (addmut){
  1038.                    out(counter, 0) = r + 1;
  1039.                    out(counter, 1) = c + 1;
  1040.                    counter += 1;
  1041.                    out(counter, 1) = r + 1;
  1042.                    out(counter, 0) = c + 1;
  1043.                    counter += 1;
  1044.                    m += 1;
  1045.                    }
  1046.                else{
  1047.                    out(counter, 0) = r + 1;
  1048.                    out(counter, 1) = c + 1;
  1049.                    counter += 1;                    
  1050.                    a += 1;
  1051.                    }
  1052.                }
  1053.            }
  1054.        return out;
  1055.        }
  1056. ')
  1057.  
  1058. cppFunction('    
  1059.    IntegerMatrix uman_game_exact_sparse_cpp(int n, IntegerVector dc){
  1060.        IntegerMatrix out((dc[0] * 2) + dc[1], 2);
  1061.        int m = 0;
  1062.        int a = 0;
  1063.        int counter = 0;
  1064.        int r = 0;
  1065.        int c = 0;
  1066.        int addmutint = 0;
  1067.        double addmutdoub = 0.0;
  1068.        bool addmut = false;
  1069.        while (counter < out.nrow()){
  1070.            r = rand() % n;
  1071.            c = rand() % n;
  1072.            bool used = false;
  1073.            int j = 0;
  1074.            while ((j < counter) & (used == false)){
  1075.                if (((out(j, 0) == (r + 1)) & (out(j, 1) == (c + 1))) | ((out(j, 0) == (c + 1)) & (out(j, 1) == (r + 1)))){
  1076.                    used = true;
  1077.                    }
  1078.                j += 1;
  1079.                }
  1080.            if ((r != c) & (used == false)){
  1081.                if ((m < dc[0]) & (a < dc[1])){
  1082.                    addmutint = rand() % 10000000 + 1;
  1083.                    addmutdoub = addmutint / 10000000.0;
  1084.                    addmut = addmutdoub > (dc[1] / (dc[0] + dc[1]));
  1085.                    }
  1086.                else if (m == dc[0]){
  1087.                    addmut = false;
  1088.                    }
  1089.                else{
  1090.                    addmut = true;
  1091.                    }
  1092.                if (addmut){
  1093.                    out(counter, 0) = r + 1;
  1094.                    out(counter, 1) = c + 1;
  1095.                    counter += 1;
  1096.                    out(counter, 1) = r + 1;
  1097.                    out(counter, 0) = c + 1;
  1098.                    counter += 1;
  1099.                    m += 1;
  1100.                    }
  1101.                else{
  1102.                    out(counter, 0) = r + 1;
  1103.                    out(counter, 1) = c + 1;
  1104.                    counter += 1;                    
  1105.                    a += 1;
  1106.                    }
  1107.                }
  1108.            }
  1109.        return out;
  1110.        }
  1111. ')
  1112.  
  1113. .uman_game_cpp <- list(prob = uman_game_prob_cpp, exact = uman_game_exact_cpp, exact_sparse = uman_game_exact_sparse_cpp)
  1114. rm(uman_game_prob_cpp, uman_game_exact_cpp, uman_game_exact_sparse_cpp)
  1115.  
  1116. uman_game <- function(n, dyad.dist, sparse = FALSE){
  1117.     # _n_ is the number of vertices in the desired graph
  1118.     # _dyad.dist_ is a vector of length three. The values correspond to Mutual, Asymmetric, and Null dyads
  1119.       # If the values are all less than or equal to 1 (and greater than 0), they're probabilities
  1120.       # If any of the values are greater than 1, they're exact edge counts
  1121.     # _sparse_ is good for large networks with low densities. It's poor for dense and small networks.
  1122.     n.dyads <- n * (n - 1) / 2
  1123.     if (any(dyad.dist > 1) & (sum(round(dyad.dist)) != n.dyads))
  1124.         stop("Number of dyads is not possible given the number of vertices. (Multiple edges are not supported.)")
  1125.     if (length(dyad.dist) != 3)
  1126.         stop("_dyad.dist_ corresponds to the dyad counts or probabilities. Its length must equal three, corresponding to mutual, asymmetric, and null dyads (in that order).")
  1127.  
  1128.     if (all(dyad.dist <= 1)){
  1129.         if (sparse){
  1130.             dyad.dist.round <- round(dyad.dist * n.dyads)
  1131.             dyad.dist.diff <- sum(dyad.dist.round) - n.dyads
  1132.             # If there's a rounding difference, adjust the nulls.
  1133.             dyad.dist.round[3] <- dyad.dist.round[3] - dyad.dist.diff
  1134.             g <- as.data.frame(.uman_game_cpp$exact_sparse(n, dyad.dist.round))
  1135.             g <- graph.data.frame(g, directed = TRUE, vertices = data.frame(name = 1:n))
  1136.             }
  1137.         else{
  1138.             dyad.dist <- round(dyad.dist, digits = 6)
  1139.             g <- .uman_game_cpp$prob(n, dyad.dist)
  1140.             g <- graph.adjacency(g, mode = "directed", diag = FALSE)
  1141.             }
  1142.         }
  1143.     else{
  1144.         dyad.dist <- round(dyad.dist)
  1145.         if (sparse)
  1146.             g <- .uman_game_cpp$exact_sparse(n, dyad.dist)
  1147.         else
  1148.             g <- .uman_game_cpp$exact(n, dyad.dist)
  1149.         g <- as.data.frame(g)
  1150.         g <- graph.data.frame(g, directed = TRUE, vertices = data.frame(name = 1:n))
  1151.         }
  1152.     return(g)
  1153.     }
  1154. uman_game <- cmpfun(uman_game)
  1155.  
  1156. # Rewires the dyads. It creates a random graph with the same:
  1157. #     Indegree and outdegree for each vertex (and density)
  1158. #     The level of reciprocity for each vertex (and dyad census)
  1159. # It will not produce multiple edges or loops
  1160. # I would not recommend this function for very large networks, as the matrix will get big.
  1161. # Later, if needed, I can create a sparse version that stores ties as an adjacency list rather than a matrix.
  1162.  
  1163. cppFunction('
  1164.    IntegerMatrix dyad_rewire_cpp(IntegerMatrix el, int nswaps){
  1165.        int ne = el.nrow();
  1166.        IntegerMatrix out(ne, 2);
  1167.        int i, j, asymcount, nd, na, d1, d2, s1, r1, s2, r2;
  1168.        int nv = 0;
  1169.        int counter = 0;
  1170.        int nmut = 0;
  1171.        int mutcount = 0;
  1172.        bool diffind, nonloop, mutswap, nonexist, nonrecipexist;
  1173.                
  1174.        for (i = 0; i < ne; ++i){
  1175.            if (el(i, 0) > nv){
  1176.                nv = el(i, 0);
  1177.                }
  1178.            if (el(i, 1) > nv){
  1179.                nv = el(i, 1);
  1180.                }
  1181.            }
  1182.        LogicalMatrix mat(nv, nv);
  1183.        for (i = 0; i < (nv - 1); ++i){
  1184.            mat(i, i) = false;
  1185.            for (j = (i + 1); j < nv; ++j){
  1186.                mat(i, j) = false;
  1187.                mat(j, i) = false;
  1188.                }
  1189.            }
  1190.        for (i = 0; i < ne; ++i){            
  1191.            mat((el(i, 0) - 1), (el(i, 1) - 1)) = true;
  1192.            if (mat((el(i, 0) - 1), (el(i, 1) - 1)) & mat((el(i, 1) - 1), (el(i, 0) - 1))){
  1193.                nmut += 1;
  1194.                }
  1195.            }
  1196.        nd = ne - nmut;
  1197.        na = nd - nmut;
  1198.        IntegerMatrix dl(nd, 2);
  1199.        asymcount = nmut;
  1200.        for (i = 0; i < (nv - 1); ++i){
  1201.            for (j = (i + 1); j < nv; ++j){
  1202.                if (mat(i, j) & mat(j, i)){
  1203.                    dl(mutcount, 0) = i + 1;
  1204.                    dl(mutcount, 1) = j + 1;
  1205.                    mutcount += 1;
  1206.                    }
  1207.                else if (mat(i, j) & (mat(j, i) == false)){
  1208.                    dl(asymcount, 0) = i + 1;
  1209.                    dl(asymcount, 1) = j + 1;
  1210.                    asymcount += 1;
  1211.                    }
  1212.                else if (mat(j, i) & (mat(i, j) == false)){
  1213.                    dl(asymcount, 0) = j + 1;
  1214.                    dl(asymcount, 1) = i + 1;
  1215.                    asymcount += 1;
  1216.                    }
  1217.                }
  1218.            }      
  1219.        
  1220.        while (counter < nswaps){
  1221.            d1 = rand() % nd;
  1222.            mutswap = d1 < nmut;
  1223.            if (mutswap){
  1224.                d2 = rand() % nmut;
  1225.                }
  1226.            else{
  1227.                d2 = rand() % na;
  1228.                d2 += nmut;
  1229.                }
  1230.            s1 = dl(d1, 0);
  1231.            r1 = dl(d1, 1);
  1232.            s2 = dl(d2, 0);
  1233.            r2 = dl(d2, 1);
  1234.            diffind = (d1 != d2);
  1235.            nonloop = ((s1 != r2) & (s2 != r1));
  1236.            nonexist = ((mat(s1 - 1, r2 - 1) == false) & (mat(s2 - 1, r1 - 1) == false));
  1237.            nonrecipexist = ((mat(r2 - 1, s1 - 1) == false) & (mat(r1 - 1, s2 - 1) == false));
  1238.            if (diffind & nonloop & nonexist & nonrecipexist){            
  1239.                if (mutswap){
  1240.                    dl(d1, 1) = r2;
  1241.                    dl(d2, 1) = r1;
  1242.                    mat(s1 - 1, r2 - 1) = true;
  1243.                    mat(s1 - 1, r1 - 1) = false;
  1244.                    mat(r2 - 1, s1 - 1) = true;
  1245.                    mat(r1 - 1, s1 - 1) = false;
  1246.                    
  1247.                    mat(s2 - 1, r1 - 1) = true;
  1248.                    mat(s2 - 1, r2 - 1) = false;
  1249.                    mat(r1 - 1, s2 - 1) = true;
  1250.                    mat(r2 - 1, s2 - 1) = false;
  1251.                    counter += 1;
  1252.                    }
  1253.                else{
  1254.                    dl(d1, 1) = r2;
  1255.                    dl(d2, 1) = r1;
  1256.                    mat(s1 - 1, r2 - 1) = true;
  1257.                    mat(s1 - 1, r1 - 1) = false;
  1258.                    mat(s2 - 1, r1 - 1) = true;
  1259.                    mat(s2 - 1, r2 - 1) = false;
  1260.                    counter += 1;
  1261.                    }
  1262.                }
  1263.            }
  1264.        counter = 0;
  1265.        for (i = 0; i < nmut; ++i){
  1266.            out(counter, 0) = dl(i, 0);
  1267.            out(counter, 1) = dl(i, 1);
  1268.            counter += 1;
  1269.            out(counter, 0) = dl(i, 1);
  1270.            out(counter, 1) = dl(i, 0);
  1271.            counter += 1;
  1272.            }
  1273.        for (i = nmut; i < nd; ++i){
  1274.            out(counter, 0) = dl(i, 0);
  1275.            out(counter, 1) = dl(i, 1);
  1276.            counter += 1;
  1277.            }
  1278.        return out;
  1279.        }
  1280. ')
  1281.  
  1282. .dyad_rewire_cpp <- dyad_rewire_cpp
  1283. rm(dyad_rewire_cpp)
  1284.  
  1285. rewire_dyads <- function(g, niter = 100){
  1286.     # _g_ is a directed igraph object
  1287.     # _niter_ is the number of dyad rewires to perform.
  1288.  
  1289.     if (is.directed(g) == FALSE)
  1290.         stop("_g_ should be a directed igraph object.")
  1291.     if (is.simple(g) == FALSE)
  1292.         stop("Graph _g_ must be simple. Multiple edges and loops are not currently supported.")
  1293.  
  1294.     gel <- get.edgelist(g, names = FALSE)
  1295.     gel <- .dyad_rewire_cpp(gel, niter)
  1296.     gel <- as.data.frame(gel)
  1297.     # Perhaps consider adding in the vertex and edge attributes here.
  1298.     newg <- graph.data.frame(gel, directed = TRUE, vertices = data.frame(name = 1:vcount(g)))
  1299.     return(newg)
  1300.     }
  1301. rewire_dyads <- cmpfun(rewire_dyads)
  1302.  
  1303. # See David Krackhardt. 1994. "Graph Theoretical Dimensions of Informal Organizations" in Kathleen Carley and Michael Prietula (eds.) _Computational Organizational Theory._ Hillsdale, NJ: Lawrence Earlbaum Associates, Inc, p. 89 - 111.
  1304.  
  1305. cppFunction('
  1306.    int lubness_con_cpp(LogicalMatrix r){
  1307.        int i, j, k, l, n, nub, viol;
  1308.        char lub, notlub;
  1309.        viol = 0;
  1310.        n = r.nrow();
  1311.        IntegerVector ub(n);
  1312.        
  1313.        if (n > 2){
  1314.            for (i = 0; i < (n - 1); i++){
  1315.                for (j = i + 1; j < n; j++){
  1316.                    nub = 0;
  1317.                    for (k = 0; k < n; k++){
  1318.                        if (r(k, i) && r(k, j)){
  1319.                            ub[nub] = k;
  1320.                            nub += 1;                            
  1321.                            }
  1322.                        }
  1323.                    lub = 0;
  1324.                    for (k = 0; (k < nub) && (!lub); k++){
  1325.                        notlub = 0;
  1326.                        for (l = 0; (l < nub) && (!notlub); l++){
  1327.                            if (!r(ub[k], ub[l])){
  1328.                                notlub += 1;
  1329.                                }
  1330.                            }
  1331.                        if (!notlub){
  1332.                            lub += 1;
  1333.                            }
  1334.                        }
  1335.                    if (!lub){
  1336.                        viol += 1;
  1337.                        }
  1338.                    }
  1339.                }
  1340.            }        
  1341.        return viol;
  1342.        }
  1343. ')
  1344.  
  1345. cppFunction('
  1346.    IntegerVector V_MaxV_Effic(int Nn, int Ne){
  1347.        IntegerVector out(2);
  1348.        out[0] = Ne - (Nn - 1);
  1349.        out[1] = (Nn * (Nn - 1)) - (Nn - 1);
  1350.        return out;
  1351.        }
  1352. ')
  1353.  
  1354. cppFunction('
  1355.    IntegerVector rep1(int N){
  1356.        IntegerVector out(N);
  1357.        for (int i = 0; i < N; i++)
  1358.            out[i] = 1;
  1359.        return out;
  1360.        }
  1361. ')
  1362.  
  1363. cppFunction('
  1364.    IntegerVector repx(int x, int N){
  1365.        IntegerVector out(N);
  1366.        for (int i = 0; i < N; i++)
  1367.            out[i] = x;
  1368.        return out;
  1369.        }
  1370. ')
  1371.  
  1372. cppFunction('
  1373.    int sumTRUE(LogicalVector x){
  1374.        int out = 0;
  1375.        int n = x.size();
  1376.  
  1377.        for (int i = 0; i < n; i++)
  1378.            if (x[i])
  1379.                out += 1;
  1380.        return out;
  1381.        }
  1382. ')
  1383.  
  1384. cppFunction('
  1385.    int intsum(IntegerVector x){
  1386.        int out = 0;
  1387.        int n = x.size();
  1388.  
  1389.        for (int i = 0; i < n; i++)
  1390.            out += x[i];
  1391.        return out;
  1392.        }
  1393. ')
  1394.  
  1395. .krackhardt_1994 <- list(lubness_con = lubness_con_cpp, v_maxv_effic = V_MaxV_Effic, utils = list(rep1 = rep1, repx = repx, sumTRUE = sumTRUE, intsum = intsum))
  1396.  
  1397. rm(lubness_con_cpp, V_MaxV_Effic, rep1, repx, sumTRUE, intsum)
  1398.  
  1399. connectedness <- function(g){
  1400.     if (is.directed(g) == FALSE)
  1401.         stop("Graph _g_ must be directed.")
  1402.     if (any(is.loop(g)))
  1403.         stop("Graph _g_ must not have loops.")
  1404.     N <- vcount(g)
  1405.     allpairs <- N * (N - 1) / 2
  1406.     eweight <- .krackhardt_1994$utils$rep1(ecount(g))
  1407.     discons <- sapply(1:(N - 1), function(x){
  1408.             targets <- (x + 1):N
  1409.             temppls <- shortest.paths(g, v = x, to = targets, mode = "all", weights = eweight)
  1410.             discon <- is.infinite(temppls)
  1411.             discon <- .krackhardt_1994$utils$sumTRUE(discon)
  1412.             return(discon)
  1413.             }        
  1414.         )
  1415.     return(1 - (.krackhardt_1994$utils$intsum(discons) / allpairs))
  1416.     }
  1417. connectedness <- cmpfun(connectedness)
  1418.  
  1419. hierarchy_krackhardt <- function(g){
  1420.     if (is.directed(g) == FALSE)
  1421.         stop("Graph _g_ must be directed.")
  1422.     if (any(is.loop(g)))
  1423.         stop("Graph _g_ must not have loops.")
  1424.     N <- vcount(g)
  1425.     eweight <- .krackhardt_1994$utils$rep1(ecount(g))
  1426.     conned <- lapply(1:N, function(x){
  1427.             targets <- c(1:N)[-x]
  1428.             temppls <- shortest.paths(g, v = x, to = targets, mode = "out", weights = eweight)
  1429.             reachable <- targets[which(is.finite(temppls))]
  1430.             reachableedges <- cbind(rep(x, times = length(reachable)), reachable)
  1431.             return(reachableedges)
  1432.             }
  1433.         )
  1434.     conned <- do.call(rbind, conned)
  1435.     conned <- graph.edgelist(as.matrix(conned), directed = TRUE)
  1436.     conneddc <- unlist(dyad.census(conned))
  1437.     return(1 - (conneddc["mut"] / (conneddc["mut"] + conneddc["asym"])))
  1438.     }
  1439. hierarchy_krackhardt <- cmpfun(hierarchy_krackhardt)
  1440.  
  1441. # The code for efficiency runs ~3x slower than the library(sna) code on the small EIES data.
  1442. efficiency <- function(g){
  1443.     if (is.directed(g) == FALSE)
  1444.         stop("Graph _g_ must be directed.")
  1445.     if (is.simple(g) == FALSE)
  1446.         stop("Graph _g_ must be a simple graph.")
  1447.     gclust <- clusters(g, mode = "weak")
  1448.     excesscount <- lapply(1:gclust$no, function(x){
  1449.             subg <- induced.subgraph(g, vids = which(gclust$membership == x))
  1450.             Ne <- ecount(subg)
  1451.             Nn <- vcount(subg)
  1452.             return(.krackhardt_1994$v_maxv_effic(Nn, Ne))
  1453.             }
  1454.         )
  1455.     excesscount <- do.call(rbind, excesscount)
  1456.     excesscount <- colSums(excesscount) # Wrote an Rcpp colSums equivalent. R's native colSums works faster.
  1457.     return(1 - (excesscount[1] / excesscount[2]))
  1458.     }
  1459. efficiency <- cmpfun(efficiency)
  1460.  
  1461. # The LUBness program was initially written by Carter T. Butts for library(sna). I have adapted the code somewhat for igraph and Rcpp.
  1462.  
  1463. lubness <- function(g){
  1464.     if (is.directed(g) == FALSE)
  1465.         stop("Graph _g_ must be directed.")
  1466.     if (any(is.loop(g)))
  1467.         stop("Graph _g_ must not have loops.")        
  1468.     cd <- clusters(g, mode = "weak")
  1469.     nolub <- 0
  1470.     maxnolub <- 0
  1471.     for (i in 1:cd$no){
  1472.         vi <- which(cd$membership == i)
  1473.         subg <- induced.subgraph(g, vi)
  1474.         ne <- ecount(subg)
  1475.         E(subg)$weight <- rep(1, times = ne) # Use Rcpp here, rep1
  1476.         nv <- vcount(subg)
  1477.         r <- lapply(1:nv, function(x){
  1478.                 sp <- shortest.paths(subg, v = x, mode = "out")
  1479.                 sp <- is.finite(sp)
  1480.                 return(sp)
  1481.                 }
  1482.             )
  1483.         r <- do.call(rbind, r)
  1484.        
  1485.         if (cd$csize[i] > 2){
  1486.             viol <- .krackhardt_1994$lubness_con(r)
  1487.             nolub <- nolub + viol
  1488.             maxnolub <- maxnolub + (cd$csize[i] - 1) * (cd$csize[i] - 2) / 2 # Use Rcpp here
  1489.             }
  1490.         }
  1491.     return(1 - nolub / maxnolub)
  1492.     }
  1493. lubness <- cmpfun(lubness)
  1494.  
  1495. # My C++ code generally performs poorer than my R code
  1496. vazquez_game<-function(N, u){
  1497.   # _N_ is the number of nodes
  1498.   # _u_ is the triadic closure parameter. Needs to be between 0 and 1.
  1499.   # Alexei Vazquez.  2003.  "Growing network with local rules: Preferential attachment, clustering hierarchy, and degree correlations."  Physical Review E 67, 056104
  1500.   add.node.edge<-function()
  1501.     return(add.node = c(rbinom(1, 1, 1 - u), close.tri = rbinom(1, 1, u)))
  1502.   vneigh <- function(v, el){
  1503.     receivers <- el[which(el[ , 1] == v), 2]
  1504.     senders <- el[which(el[ , 2] == v), 1]
  1505.     return(unique(c(receivers, senders)))
  1506.     }
  1507.   g <- matrix(NA, 0, 2)
  1508.   potentials <- matrix(NA, 0, 2)
  1509.   ncount <- 1
  1510.   while(ncount < N){
  1511.     a <- add.node.edge()
  1512.     if (a[1] == 1){
  1513.       source.v <- ncount + 1
  1514.       target.v <- ifelse(ncount > 1, sample.int(ncount, 1), 1)
  1515.       target.neighbors <- vneigh(target.v, g)
  1516.       g <- rbind(g, c(source.v, target.v))
  1517.       n.neighbors <- length(target.neighbors)
  1518.       if (n.neighbors > 0){
  1519.         new.potentials <- matrix(NA, n.neighbors, 2)
  1520.         new.potentials[ , 1] <- source.v
  1521.         new.potentials[ , 2] <- target.neighbors
  1522.         potentials <- rbind(potentials, new.potentials)
  1523.         rm(new.potentials)
  1524.         }
  1525.       rm(source.v, target.v, target.neighbors, n.neighbors)
  1526.       ncount <- ncount + 1
  1527.       }
  1528.     if ((a[2] == 1) & (ncount > 2) & (length(potentials) >= 2)){
  1529.       if (length(potentials) == 2){
  1530.         g <- rbind(g, c(potentials[1], potentials[2]))
  1531.         potentials <- matrix(NA, 0, 2)
  1532.         }
  1533.       else{
  1534.         potentials.ind <- sample.int(nrow(potentials), 1)
  1535.         adders <- potentials[potentials.ind, ]
  1536.         g <- rbind(g, c(adders[1], adders[2]))
  1537.         potentials <- potentials[-potentials.ind, ]
  1538.         }
  1539.       }    
  1540.     }
  1541.   g <- graph.edgelist(g, directed = FALSE)
  1542.   return(g)
  1543.   }
  1544. vazquez_game<-cmpfun(vazquez_game)
Advertisement
Add Comment
Please, Sign In to add comment