R/models.R

Defines functions summary.netlogit summary.netlm summary.netcancor summary.lnam summary.brokerage summary.bn summary.bbnam.pooled summary.bbnam.fixed summary.bbnam.actor summary.bbnam summary.bayes.factor se.lnam pstar print.summary.netlogit print.summary.netlm print.summary.netcancor print.summary.lnam print.summary.brokerage print.summary.bn print.summary.bbnam.pooled print.summary.bbnam.fixed print.summary.bbnam.actor print.summary.bbnam print.summary.bayes.factor print.netlogit print.netlm print.netcancor print.lnam print.bn print.bbnam.pooled print.bbnam.fixed print.bbnam.actor print.bbnam print.bayes.factor potscalered.mcmc plot.lnam plot.bn plot.bbnam.pooled plot.bbnam.fixed plot.bbnam.actor plot.bbnam npostpred netlogit netlm netcancor nacf lnam eval.edgeperturbation consensus coef.lnam coef.bn brokerage bn.nltl bn.nlpl.triad bn.nlpl.edge bn.nlpl.dyad bn bbnam.probtie bbnam.pooled bbnam.jntlik.slice bbnam.jntlik bbnam.fixed bbnam.bf bbnam.actor bbnam

Documented in bbnam bbnam.actor bbnam.bf bbnam.fixed bbnam.jntlik bbnam.jntlik.slice bbnam.pooled bbnam.probtie bn bn.nlpl.dyad bn.nlpl.edge bn.nlpl.triad bn.nltl brokerage coef.bn coef.lnam consensus eval.edgeperturbation lnam nacf netcancor netlm netlogit npostpred plot.bbnam plot.bbnam.actor plot.bbnam.fixed plot.bbnam.pooled plot.bn plot.lnam potscalered.mcmc print.bayes.factor print.bbnam print.bbnam.actor print.bbnam.fixed print.bbnam.pooled print.bn print.lnam print.netcancor print.netlm print.netlogit print.summary.bayes.factor print.summary.bbnam print.summary.bbnam.actor print.summary.bbnam.fixed print.summary.bbnam.pooled print.summary.bn print.summary.brokerage print.summary.lnam print.summary.netcancor print.summary.netlm print.summary.netlogit pstar se.lnam summary.bayes.factor summary.bbnam summary.bbnam.actor summary.bbnam.fixed summary.bbnam.pooled summary.bn summary.brokerage summary.lnam summary.netcancor summary.netlm summary.netlogit

######################################################################
#
# models.R
#
# copyright (c) 2004, Carter T. Butts <buttsc@uci.edu>
# Last Modified 7/18/16
# Licensed under the GNU General Public License version 2 (June, 1991)
# or later.
#
# Part of the R/sna package
#
# This file contains routines related to stochastic models.
#
# Contents:
#   bbnam
#   bbnam.actor
#   bbnam.bf
#   bbnam.fixed
#   bbnam.jntlik
#   bbnam.jntlik.slice
#   bbnam.pooled
#   bn
#   bn.nlpl.dyad
#   bn.nlpl.edge
#   bn.nlpl.triad
#   bn.nltl
#   brokerage
#   coef.bn
#   coef.lnam
#   consensus
#   eval.edgeperturbation
#   lnam
#   nacf
#   netcancor
#   netlm
#   netlogit
#   npostpred
#   potscalered.mcmc
#   plot.bbnam
#   plot.bbnam.actor
#   plot.bbnam.fixed
#   plot.bbnam.pooled
#   plot.bn
#   plot.lnam
#   print.bbnam
#   print.bbnam.actor
#   print.bbnam.fixed
#   print.bbnam.pooled
#   print.bn
#   print.lnam
#   print.netcancor
#   print.netlm
#   print.netlogit
#   print.summary.bbnam
#   print.summary.bbnam.actor
#   print.summary.bbnam.fixed
#   print.summary.bbnam.pooled
#   print.summary.bn
#   print.summary.brokerage
#   print.summary.lnam
#   print.summary.netcancor
#   print.summary.netlm
#   print.summary.netlogit
#   pstar
#   se.lnam
#   summary.bbnam
#   summary.bbnam.actor
#   summary.bbnam.fixed
#   summary.bbnam.pooled
#   summary.bn
#   summary.brokerage
#   summary.lnam
#   summary.netcancor
#   summary.netlm
#   summary.netlogit 
#
######################################################################


#bbnam - Draw from Butts' Bayesian Network Accuracy Model.  This version uses a 
#Gibbs' Sampler, and assumes error rates to be drawn from conditionally 
#independent betas for each actor.  Note that dat MUST be an n x n x n array, 
#and that the data in question MUST be dichotomous.  Priors are also assumed to 
#be in the right form (n x 2 matrices of alpha, beta pairs for em and ep, and
#an n x n probability matrix for the network itself), and are not checked; 
#default behavior if no priors are provided is the uninformative case.
#Wrapper function for the various bbnam models
bbnam<-function(dat,model="actor",...){
   if(model=="actor")
      bbnam.actor(dat,...)
   else if(model=="pooled")
      bbnam.pooled(dat,...)
   else if(model=="fixed")
      bbnam.fixed(dat,...)
}


#bbnam.actor - Draw from the error-prob-by-actor model
bbnam.actor<-function(dat,nprior=0.5,emprior=c(1,11),epprior=c(1,11),diag=FALSE, mode="digraph",reps=5,draws=1500,burntime=500,quiet=TRUE,anames=NULL,onames=NULL,compute.sqrtrhat=TRUE){
   dat<-as.sociomatrix.sna(dat,simplify=TRUE)
   if(is.list(dat))
     stop("All bbnam input graphs must be of the same order.")
   if(length(dim(dat))==2)
     dat<-array(dat,dim=c(1,NROW(dat),NCOL(dat)))
   #First, collect some basic model parameters and do other "setup" stuff
   if(reps==1)
     compute.sqrtrhat<-FALSE  #Can't use Rhat if we have only one chain!
   m<-dim(dat)[1]
   n<-dim(dat)[2]
   d<-dat
   slen<-burntime+floor(draws/reps)
   out<-list()
   if((!is.matrix(nprior))||(NROW(nprior)!=n)||(NCOL(nprior)!=n))
     nprior<-matrix(nprior,n,n)
   if((!is.matrix(emprior))||(NROW(emprior)!=m)||(NCOL(emprior)!=2)){
     if(length(emprior)==2)
       emprior<-sapply(emprior,rep,m)
     else
       emprior<-matrix(emprior,m,2)
   }
   if((!is.matrix(epprior))||(NROW(epprior)!=m)||(NCOL(epprior)!=2)){
     if(length(epprior)==2)
       epprior<-sapply(epprior,rep,m)
     else
       epprior<-matrix(epprior,m,2)
   }
   if(is.null(anames))
     anames<-paste("a",1:n,sep="")
   if(is.null(onames))
     onames<-paste("o",1:m,sep="")
   #Remove any data which doesn't count...
   if(mode=="graph")
      d<-upper.tri.remove(d)
   if(!diag)
      d<-diag.remove(d)
   #OK, let's get started.  First, create temp variables to hold draws, and draw
   #initial conditions for the Markov chain
   if(!quiet)
      cat("Creating temporary variables and drawing initial conditions....\n")
   a<-array(dim=c(reps,slen,n,n))
   em<-array(dim=c(reps,slen,m))
   ep<-array(dim=c(reps,slen,m))
   for(k in 1:reps){
      a[k,1,,]<-rgraph(n,1,diag=diag,mode=mode)
      em[k,1,]<-runif(m,0,0.5)
      ep[k,1,]<-runif(m,0,0.5)
   }
   #Let the games begin: draw from the Gibbs' sampler
   for(i in 1:reps){
      for(j in 2:slen){
         if(!quiet)
            cat("Repetition",i,", draw",j,":\n\tDrawing adjacency matrix\n")
         #Create tie probability matrix
         ep.a<-aperm(array(sapply(ep[i,j-1,],rep,n^2),dim=c(n,n,m)),c(3,2,1))
         em.a<-aperm(array(sapply(em[i,j-1,],rep,n^2),dim=c(n,n,m)),c(3,2,1))
         pygt<-apply(d*(1-em.a)+(1-d)*em.a,c(2,3),prod,na.rm=TRUE)
         pygnt<-apply(d*ep.a+(1-d)*(1-ep.a),c(2,3),prod,na.rm=TRUE)
         tieprob<-(nprior*pygt)/(nprior*pygt+(1-nprior)*pygnt)
         if(mode=="graph")
           tieprob[upper.tri(tieprob)]<-t(tieprob)[upper.tri(tieprob)]
         #Draw Bernoulli graph
         a[i,j,,]<-rgraph(n,1,tprob=tieprob,mode=mode,diag=diag)
         if(!quiet)
            cat("\tAggregating binomial counts\n")
         cem<-matrix(nrow=m,ncol=2)
         cep<-matrix(nrow=m,ncol=2)
         for(x in 1:m){
               cem[x,1]<-sum((1-d[x,,])*a[i,j,,],na.rm=TRUE)
               cem[x,2]<-sum(d[x,,]*a[i,j,,],na.rm=TRUE)
               cep[x,1]<-sum(d[x,,]*(1-a[i,j,,]),na.rm=TRUE)
               cep[x,2]<-sum((1-d[x,,])*(1-a[i,j,,]),na.rm=TRUE)
         }
         if(!quiet)
            cat("\tDrawing error parameters\n")
         em[i,j,]<-rbeta(m,emprior[,1]+cem[,1],emprior[,2]+cem[,2])
         ep[i,j,]<-rbeta(m,epprior[,1]+cep[,1],epprior[,2]+cep[,2])
      }
   }
   if(!quiet)
      cat("Finished drawing from Markov chain.  Now computing potential scale reduction statistics.\n")
   if(compute.sqrtrhat){
      out$sqrtrhat<-vector()
      for(i in 1:n)
         for(j in 1:n)
            out$sqrtrhat<-c(out$sqrtrhat,potscalered.mcmc(aperm(a,c(2,1,3,4))[,,i,j]))
      for(i in 1:m)
         out$sqrtrhat<-c(out$sqrtrhat,potscalered.mcmc(aperm(em,c(2,1,3))[,,i]),potscalered.mcmc(aperm(ep,c(2,1,3))[,,i]))
      if(!quiet)
         cat("\tMax potential scale reduction (Gelman et al.'s sqrt(Rhat)) for all scalar estimands:",max(out$sqrtrhat[!is.nan(out$sqrtrhat)],na.rm=TRUE),"\n")
   }
   if(!quiet)
      cat("Preparing output.\n")
   #Whew, we're done with the MCMC.  Now, let's get that data together.
   out$net<-array(dim=c(reps*(slen-burntime),n,n))
   for(i in 1:reps)
      for(j in burntime:slen){
         out$net[(i-1)*(slen-burntime)+(j-burntime),,]<-a[i,j,,]
      }
   if(!quiet)
      cat("\tAggregated network variable draws\n")
   out$em<-em[1,(burntime+1):slen,]
   out$ep<-ep[1,(burntime+1):slen,]
   if(reps>=2)
      for(i in 2:reps){
         out$em<-rbind(out$em,em[i,(burntime+1):slen,])
         out$ep<-rbind(out$ep,ep[i,(burntime+1):slen,])
      }
   if(!quiet)
      cat("\tAggregated error parameters\n")
   #Finish off the output and return it.
   out$anames<-anames
   out$onames<-onames
   out$nactors<-n
   out$nobservers<-m
   out$reps<-reps
   out$draws<-dim(out$em)[1]
   out$burntime<-burntime
   out$model<-"actor"
   class(out)<-c("bbnam.actor","bbnam")
   out
}


#bbnam.bf - Estimate Bayes Factors for the Butts Bayesian Network Accuracy 
#Model.  This implementation relies on monte carlo integration to estimate the 
#BFs, and tests the fixed probability, pooled, and pooled by actor models.
bbnam.bf<-function(dat,nprior=0.5,em.fp=0.5,ep.fp=0.5,emprior.pooled=c(1,11),epprior.pooled=c(1,11),emprior.actor=c(1,11),epprior.actor=c(1,11),diag=FALSE, mode="digraph",reps=1000){
   dat<-as.sociomatrix.sna(dat,simplify=TRUE)
   if(is.list(dat))
     stop("All bbnam.bf input graphs must be of the same order.")
   if(length(dim(dat))==2)
     dat<-array(dat,dim=c(1,NROW(dat),NCOL(dat)))
   m<-dim(dat)[1]
   n<-dim(dat)[2]
   if((!is.matrix(nprior))||(NROW(nprior)!=n)||(NCOL(nprior)!=n))
     nprior<-matrix(nprior,n,n)
   if((!is.matrix(emprior.actor))||(NROW(emprior.actor)!=m)|| (NCOL(emprior.actor)!=2)){
     if(length(emprior.actor)==2)
       emprior.actor<-sapply(emprior.actor,rep,m)
     else
       emprior.actor<-matrix(emprior.actor,m,2)
   }
   if((!is.matrix(epprior.actor))||(NROW(epprior.actor)!=m)|| (NCOL(epprior.actor)!=2)){
     if(length(epprior.actor)==2)
       epprior.actor<-sapply(epprior.actor,rep,m)
     else
       epprior.actor<-matrix(epprior.actor,m,2)
   }
   d<-dat
   if(!diag)
      d<-diag.remove(d)
   if(mode=="graph")
      d<-lower.tri.remove(d)
   pfpv<-vector()
   ppov<-vector()
   pacv<-vector()
   #Draw em, ep, and a values for the various models
   for(i in 1:reps){
      a<-rgraph(n,1,tprob=nprior)
      em.pooled<-eval(call("rbeta",1,emprior.pooled[1],emprior.pooled[2]))
      ep.pooled<-eval(call("rbeta",1,epprior.pooled[1],epprior.pooled[2]))
      em.actor<-eval(call("rbeta",n,emprior.actor[,1],emprior.actor[,2]))
      ep.actor<-eval(call("rbeta",n,epprior.actor[,1],epprior.actor[,2]))
      pfpv[i]<-bbnam.jntlik(d,a=a,em=em.fp,ep=ep.fp,log=TRUE)
      ppov[i]<-bbnam.jntlik(d,a=a,em=em.pooled,ep=ep.pooled,log=TRUE)
      pacv[i]<-bbnam.jntlik(d,a=a,em=em.actor,ep=ep.actor,log=TRUE)
   }
   int.lik<-c(logMean(pfpv),logMean(ppov),logMean(pacv))
   int.lik.std<-sqrt(c(var(pfpv),var(ppov),var(pacv)))
   int.lik.std<-(logSub(c(logMean(2*pfpv),logMean(2*ppov),logMean(2*pacv)), 2*int.lik)-log(reps))/2
   #Find the Bayes Factors
   o<-list()
   o$int.lik<-matrix(nrow=3,ncol=3)
   for(i in 1:3)
      for(j in 1:3){
         if(i!=j)
            o$int.lik[i,j]<-int.lik[i]-int.lik[j]
         else
            o$int.lik[i,i]<-int.lik[i]
      }
   o$int.lik.std<-int.lik.std
   o$reps<-reps
   o$prior.param<-list(nprior,em.fp,ep.fp,emprior.pooled,epprior.pooled,emprior.actor,epprior.actor)
   o$prior.param.names<-c("nprior","em.fp","ep.fp","emprior.pooled","epprior.pooled","emprior.actor","epprior.actor")
   o$model.names<-c("Fixed Error Prob","Pooled Error Prob","Actor Error Prob")
   class(o)<-c("bbnam.bf","bayes.factor")
   o
}


#bbnam.fixed - Draw from the fixed probability error model
bbnam.fixed<-function(dat,nprior=0.5,em=0.25,ep=0.25,diag=FALSE,mode="digraph",draws=1500,outmode="draws",anames=NULL,onames=NULL){
   dat<-as.sociomatrix.sna(dat,simplify=TRUE)
   if(is.list(dat))
     stop("All bbnam input graphs must be of the same order.")
   if(length(dim(dat))==2)
     dat<-array(dat,dim=c(1,NROW(dat),NCOL(dat)))
   #How many actors are involved?
   m<-dim(dat)[1]
   n<-dim(dat)[2]
   if((!is.matrix(nprior))||(NROW(nprior)!=n)||(NCOL(nprior)!=n))
     nprior<-matrix(nprior,n,n)
   if(is.null(anames))
     anames<-paste("a",1:n,sep="")
   if(is.null(onames))
     onames<-paste("o",1:m,sep="")
   #Check to see if we've been given full matrices (or vectors) of error probs...
   if(length(em)==m*n^2)
      em.a<-em
   else if(length(em)==n^2)
      em.a<-apply(em,c(1,2),rep,m)
   else if(length(em)==m)
      em.a<-aperm(array(sapply(em,rep,n^2),dim=c(n,n,m)),c(3,2,1))
   else if(length(em)==1)
      em.a<-array(rep(em,m*n^2),dim=c(m,n,n))
   if(length(ep)==m*n^2)
      ep.a<-ep
   else if(length(ep)==n^2)
      ep.a<-apply(ep,c(1,2),rep,m)
   else if(length(ep)==m)
      ep.a<-aperm(array(sapply(ep,rep,n^2),dim=c(n,n,m)),c(3,2,1))
   else if(length(ep)==1)
      ep.a<-array(rep(ep,m*n^2),dim=c(m,n,n))
   #Find the network posterior
   pygt<-apply(dat*(1-em.a)+(1-dat)*em.a,c(2,3),prod,na.rm=TRUE)
   pygnt<-apply(dat*ep.a+(1-dat)*(1-ep.a),c(2,3),prod,na.rm=TRUE)
   npost<-(nprior*pygt)/(nprior*pygt+(1-nprior)*pygnt)
   #Send the needed output
   if(outmode=="posterior")
      npost
   else{
      o<-list()
      o$net<-rgraph(n,draws,tprob=npost,diag=diag,mode=mode)
      o$anames<-anames
      o$onames<-onames
      o$nactors<-n
      o$nobservers<-m
      o$draws<-draws
      o$model<-"fixed"
      class(o)<-c("bbnam.fixed","bbnam")
      o
   }
}


#bbnam.jntlik - An internal function for bbnam
bbnam.jntlik<-function(dat,log=FALSE,...){
   p<-sum(sapply(1:dim(dat)[1],bbnam.jntlik.slice,dat=dat,log=TRUE,...))
   if(!log)
      exp(p)
   else
      p
}


#bbnam.jntlik.slice - An internal function for bbnam
bbnam.jntlik.slice<-function(s,dat,a,em,ep,log=FALSE){
   if(length(em)>1)
      em.l<-em[s]
   else
      em.l<-em
   if(length(ep)>1)
      ep.l<-ep[s]
   else
      ep.l<-ep
   p<-sum(log((1-a)*(dat[s,,]*ep.l+(1-dat[s,,])*(1-ep.l))+a*(dat[s,,]*(1-em.l)+(1-dat[s,,])*em.l)),na.rm=TRUE)
   if(!log)
      exp(p)
   else
      p
}


#bbnam.pooled - Draw from the pooled error model
bbnam.pooled<-function(dat,nprior=0.5,emprior=c(1,11),epprior=c(1,11), diag=FALSE,mode="digraph",reps=5,draws=1500,burntime=500,quiet=TRUE,anames=NULL,onames=NULL,compute.sqrtrhat=TRUE){
   dat<-as.sociomatrix.sna(dat,simplify=TRUE)
   if(is.list(dat))
     stop("All bbnam input graphs must be of the same order.")
   if(length(dim(dat))==2)
     dat<-array(dat,dim=c(1,NROW(dat),NCOL(dat)))
   #First, collect some basic model parameters and do other "setup" stuff
   if(reps==1)
     compute.sqrtrhat<-FALSE  #Can't use Rhat if we have only one chain!
   m<-dim(dat)[1]
   n<-dim(dat)[2]
   d<-dat
   slen<-burntime+floor(draws/reps)
   out<-list()
   if((!is.matrix(nprior))||(NROW(nprior)!=n)||(NCOL(nprior)!=n))
     nprior<-matrix(nprior,n,n)
   if(is.null(anames))
     anames<-paste("a",1:n,sep="")
   if(is.null(onames))
     onames<-paste("o",1:m,sep="")
   #Remove any data which doesn't count...
   if(mode=="graph")
      d<-upper.tri.remove(d)
   if(!diag)
      d<-diag.remove(d)
   #OK, let's get started.  First, create temp variables to hold draws, and draw
   #initial conditions for the Markov chain
   if(!quiet)
      cat("Creating temporary variables and drawing initial conditions....\n")
   a<-array(dim=c(reps,slen,n,n))
   em<-array(dim=c(reps,slen))
   ep<-array(dim=c(reps,slen))
   for(k in 1:reps){
      a[k,1,,]<-rgraph(n,1,diag=diag,mode=mode)
      em[k,1]<-runif(1,0,0.5)
      ep[k,1]<-runif(1,0,0.5)
   }
   #Let the games begin: draw from the Gibbs' sampler
   for(i in 1:reps){
      for(j in 2:slen){
         if(!quiet)
            cat("Repetition",i,", draw",j,":\n\tDrawing adjacency matrix\n")
         #Create tie probability matrix
         ep.a<-array(rep(ep[i,j-1],m*n^2),dim=c(m,n,n))
         em.a<-array(rep(em[i,j-1],m*n^2),dim=c(m,n,n))
         pygt<-apply(d*(1-em.a)+(1-d)*em.a,c(2,3),prod,na.rm=TRUE)
         pygnt<-apply(d*ep.a+(1-d)*(1-ep.a),c(2,3),prod,na.rm=TRUE)
         tieprob<-(nprior*pygt)/(nprior*pygt+(1-nprior)*pygnt)
         if(mode=="graph")
           tieprob[upper.tri(tieprob)]<-t(tieprob)[upper.tri(tieprob)]
         #Draw Bernoulli graph
         a[i,j,,]<-rgraph(n,1,tprob=tieprob,mode=mode,diag=diag)
         if(!quiet)
            cat("\tAggregating binomial counts\n")
         cem<-vector(length=2)
         cep<-vector(length=2)
         a.a<-apply(a[i,j,,],c(1,2),rep,m)
         cem[1]<-sum((1-d)*a.a,na.rm=TRUE)
         cem[2]<-sum(d*a.a,na.rm=TRUE)
         cep[1]<-sum(d*(1-a.a),na.rm=TRUE)
         cep[2]<-sum((1-d)*(1-a.a),na.rm=TRUE)
         #cat("em - alpha",cem[1],"beta",cem[2]," ep - alpha",cep[1],"beta",cep[2],"\n")
         if(!quiet)
            cat("\tDrawing error parameters\n")
         em[i,j]<-rbeta(1,emprior[1]+cem[1],emprior[2]+cem[2])
         ep[i,j]<-rbeta(1,epprior[1]+cep[1],epprior[2]+cep[2])
      }
   }
   if(!quiet)
      cat("Finished drawing from Markov chain.  Now computing potential scale reduction statistics.\n")
   if(compute.sqrtrhat){
      out$sqrtrhat<-vector()
      for(i in 1:n)
         for(j in 1:n)
            out$sqrtrhat<-c(out$sqrtrhat,potscalered.mcmc(aperm(a,c(2,1,3,4))[,,i,j]))
      out$sqrtrhat<-c(out$sqrtrhat,potscalered.mcmc(em),potscalered.mcmc(ep))
   if(!quiet)
      cat("\tMax potential scale reduction (Gelman et al.'s sqrt(Rhat)) for all scalar estimands:",max(out$sqrtrhat[!is.nan(out$sqrtrhat)],na.rm=TRUE),"\n")
   }
   if(!quiet)
      cat("Preparing output.\n")
   #Whew, we're done with the MCMC.  Now, let's get that data together.
   out$net<-array(dim=c(reps*(slen-burntime),n,n))
   for(i in 1:reps)
      for(j in burntime:slen){
         out$net[(i-1)*(slen-burntime)+(j-burntime),,]<-a[i,j,,]
      }
   if(!quiet)
      cat("\tAggregated network variable draws\n")
   out$em<-em[1,(burntime+1):slen]
   out$ep<-ep[1,(burntime+1):slen]
   if(reps>=2)
      for(i in 2:reps){
         out$em<-c(out$em,em[i,(burntime+1):slen])
         out$ep<-c(out$ep,ep[i,(burntime+1):slen])
      }
   if(!quiet)
      cat("\tAggregated error parameters\n")
   #Finish off the output and return it.
   out$anames<-anames
   out$onames<-onames
   out$nactors<-n
   out$nobservers<-m
   out$reps<-reps
   out$draws<-length(out$em)
   out$burntime<-burntime
   out$model<-"pooled"
   class(out)<-c("bbnam.pooled","bbnam")
   out
}


#bbnam.probtie - Probability of a given tie
bbnam.probtie<-function(dat,i,j,npriorij,em,ep){
   num<-npriorij
   denom<-1-npriorij
   num<-num*prod(dat[,i,j]*(1-em)+(1-dat[,i,j])*em,na.rm=TRUE)
   denom<-denom*prod(dat[,i,j]*ep+(1-dat[,i,j])*(1-ep),na.rm=TRUE)
   p<-num/(denom+num)
   p
}


#bn - Fit a biased net model
bn<-function(dat,method=c("mple.triad","mple.dyad","mple.edge","mtle"),param.seed=NULL,param.fixed=NULL,optim.method="BFGS",optim.control=list(),epsilon=1e-5){
  dat<-as.sociomatrix.sna(dat,simplify=FALSE)
  if(is.list(dat))
    return(lapply(dat,bn,method=method,param.seed=param.seed, param.fixed=param.fixed,optim.method=optim.method,optim.contol=optim.control,epsilon=epsilon))
  else if(length(dim(dat))>2)
    return(apply(dat,1,bn,method=method,param.seed=param.seed, param.fixed=param.fixed,optim.method=optim.method,optim.contol=optim.control,epsilon=epsilon))
  n<-NROW(dat)
  #Make sure dat is appropriate
  if(!is.matrix(dat))
    stop("Adjacency matrix required in bn.")
  dat<-dat>0
  #Choose the objective function to use
  nll<-switch(match.arg(method),
    mple.edge=match.fun("bn.nlpl.edge"),
    mple.dyad=match.fun("bn.nlpl.dyad"),
    mple.triad=match.fun("bn.nlpl.triad"),
    mtle=match.fun("bn.nltl")
  )
  #Extract the necessary sufficient statistics
  if(match.arg(method)%in%c("mple.edge","mple.dyad")){  #Use dyad census stats
    stats<-matrix(0,nrow=n-1,ncol=4)
    stats<-matrix(.C("bn_dyadstats_R",as.integer(dat),as.double(n), stats=as.double(stats),PACKAGE="sna")$stats,ncol=4)
    stats<-stats[apply(stats[,2:4],1,sum)>0,]  #Strip uneeded rows
  }else if(match.arg(method)=="mple.triad"){          #Use full dyad stats
    stats<-matrix(0,nrow=n,ncol=n)
    stats<-matrix(.C("bn_triadstats_R",as.integer(dat),as.double(n), stats=as.double(stats),PACKAGE="sna")$stats,nrow=n,ncol=n)
  }else if(match.arg(method)=="mtle"){                #Use triad census stats
    stats<-as.vector(triad.census(dat))  #Obtain triad census
  }
  #Initialize parameters (using crudely reasonable values)
  if(is.null(param.seed))
    param<-c(gden(dat),grecip(dat,measure="edgewise"),gtrans(dat),gtrans(dat))
  else{
    param<-c(gden(dat),grecip(dat,measure="edgewise"),gtrans(dat),gtrans(dat))
    if(!is.null(param.seed$pi))
      param[1]<-param.seed$pi
    if(!is.null(param.seed$sigma))
      param[2]<-param.seed$sigma
    if(!is.null(param.seed$rho))
      param[3]<-param.seed$rho
    if(!is.null(param.seed$d))
      param[4]<-param.seed$d
  }
  if(is.null(param.fixed))   #Do we need to fix certain parameter values?
    fixed<-rep(NA,4)
  else{
    fixed<-rep(NA,4)
    if(!is.null(param.fixed$pi))
      fixed[1]<-param.fixed$pi
    if(!is.null(param.fixed$sigma))
      fixed[2]<-param.fixed$sigma
    if(!is.null(param.fixed$rho))
      fixed[3]<-param.fixed$rho
    if(!is.null(param.fixed$d))
      fixed[4]<-param.fixed$d
  }
  param<-pmax(pmin(param,1-epsilon),epsilon)  #Ensure interior starting vals
  param<-log(param/(1-param))  #Transform to logit scale
  #Fit the model
  fit<-optim(param,nll,method=optim.method,control=optim.control,stats=stats, fixed=fixed,dat=dat)
  fit$par<-1/(1+exp(-fit$par))                 #Untransform
  fit$par[!is.na(fixed)]<-fixed[!is.na(fixed)] #Fix
  #Prepare the results
  out<-list(d=fit$par[4],pi=fit$par[1],sigma=fit$par[2],rho=fit$par[3], method=match.arg(method),G.square=2*fit$value,epsilon=epsilon)
  #Add GOF for triads
  if(match.arg(method)=="mtle")
    out$triads<-stats
  else
    out$triads<-as.vector(triad.census(dat))
  out$triads.pred<-.C("bn_ptriad_R", as.double(out$pi),as.double(out$sigma),as.double(out$rho), as.double(out$d),pt=as.double(rep(0,16)),PACKAGE="sna")$pt
  names(out$triads.pred)<-c("003", "012", "102", "021D", "021U", "021C", "111D", "111U", "030T", "030C", "201", "120D", "120U", "120C", "210", "300")
  names(out$triads)<-names(out$triads.pred)
  #Add GOF for dyads, using triad distribution
  if(match.arg(method)%in%c("mple.edge","mple.dyad"))
    out$dyads<-apply(stats[,-1],2,sum)
  else
    out$dyads<-as.vector(dyad.census(dat))
  out$dyads.pred<-c(sum(out$triads.pred*c(0,0,1,0,0,0,1,1,0,0,2,1,1,1,2,3)), sum(out$triads.pred*c(0,1,0,2,2,2,1,1,3,3,0,2,2,2,1,0)), sum(out$triads.pred*c(3,2,2,1,1,1,1,1,0,0,1,0,0,0,0,0)))*choose(n,3)/choose(n,2)/(n-2)
  names(out$dyads.pred)<-c("Mut","Asym","Null")
  names(out$dyads)<-names(out$dyads.pred)
  #Add GOF for edges, using dyad distribution
  out$edges<-c(2*out$dyads[1]+out$dyads[2],2*out$dyads[3]+out$dyads[2])
  out$edges.pred<-c(2*out$dyads.pred[1]+out$dyads.pred[2], 2*out$dyads.pred[3]+out$dyads.pred[2])/2
  names(out$edges.pred)<-c("Present","Absent")
  names(out$edges)<-names(out$edges.pred)
  #Add predicted structure statistics (crude)
  a<-out$d*(n-1)
  out$ss.pred<-c(1/n,(1-1/n)*(1-exp(-a/n)))
  for(i in 2:(n-1))
    out$ss.pred<-c(out$ss.pred,(1-sum(out$ss.pred[1:i])) * (1-exp(-(a-out$pi-out$sigma*(a-1))*out$ss.pred[i])))
  out$ss.pred<-cumsum(out$ss.pred)
  names(out$ss.pred)<-0:(n-1)
  out$ss<-structure.statistics(dat)
  #Return the result
  class(out)<-"bn"
  out
}


#bn.nlpl.dyad - Compute the dyadic -log pseudolikelihood for a biased net model
bn.nlpl.dyad<-function(p,stats,fixed=rep(NA,4),...){
  p<-1/(1+exp(-p))
  #Correct for any fixed parameters
  p[!is.na(fixed)]<-fixed[!is.na(fixed)]
  #Calculate the pseudolikelihood
  lpl<-0
  lpl<-.C("bn_lpl_dyad_R",as.double(stats),as.double(NROW(stats)), as.double(p[1]),as.double(p[2]),as.double(p[3]),as.double(p[4]), lpl=as.double(lpl),PACKAGE="sna")$lpl
  -lpl
}


#bn.nlpl.edge - Compute the -log pseudolikelihood for a biased net model,
#using the directed edge pseudolikelihood.  Not sure why you'd want to
#do this, except as a check on the other results....
bn.nlpl.edge<-function(p,stats,fixed=rep(NA,4),...){
  p<-1/(1+exp(-p))
  #Correct for any fixed parameters
  p[!is.na(fixed)]<-fixed[!is.na(fixed)]
  #Calculate the pseudolikelihood
  lp<-cbind(1-(1-p[1])*((1-p[3])^stats[,1])*((1-p[2])^stats[,1])*(1-p[4]),
    1-((1-p[2])^stats[,1])*(1-p[4]))
  lp<-log(cbind(lp,1-lp))
  lpl<-cbind(2*stats[,2]*lp[,1],stats[,3]*lp[,2],stats[,3]*lp[,3], 2*stats[,4]*lp[,4])
  lpl[is.nan(lpl)]<-0  #Treat 0 * -Inf as 0
  -sum(lpl)
}


#bn.nlpl.triad - Compute the triadic -log pseudolikelihood for a biased net
#model, using the Skvoretz (2003) working paper method
bn.nlpl.triad<-function(p,dat,stats,fixed=rep(NA,4),...){
  p<-1/(1+exp(-p))
  #Correct for any fixed parameters
  p[!is.na(fixed)]<-fixed[!is.na(fixed)]
  #Calculate the pseudolikelihood
  lpl<-0
  lpl<-.C("bn_lpl_triad_R",as.integer(dat),as.double(stats), as.double(NROW(stats)),as.double(p[1]),as.double(p[2]),as.double(p[3]), as.double(p[4]), lpl=as.double(lpl),PACKAGE="sna")$lpl
  -lpl
}


#bn.nltl - Compute the -log triad likelihood for a biased net model
bn.nltl<-function(p,stats,fixed=rep(NA,4),...){
  p<-1/(1+exp(-p))
  #Correct for any fixed parameters
  p[!is.na(fixed)]<-fixed[!is.na(fixed)]
  #Calculate the triad likelihood
  pt<-rep(0,16)
  triprob<-.C("bn_ptriad_R", as.double(p[1]),as.double(p[2]),as.double(p[3]), as.double(p[4]),pt=as.double(pt),PACKAGE="sna")$pt
  -sum(stats*log(triprob))
}


#brokerage - perform a Gould-Fernandez brokerage analysis
brokerage<-function(g,cl){
  #Pre-process the raw input
  g<-as.edgelist.sna(g)
  if(is.list(g))
    return(lapply(g,brokerage,cl))
  #End pre-processing
  N<-attr(g,"n")
  m<-NROW(g)
  classes<-unique(cl)
  icl<-match(cl,classes)
  #Compute individual brokerage measures
  br<-matrix(0,N,5)
  br<-matrix(.C("brokerage_R",as.double(g),as.integer(N),as.integer(m), as.integer(icl), brok=as.double(br),PACKAGE="sna",NAOK=TRUE)$brok,N,5)
  br<-cbind(br,apply(br,1,sum))
  #Global brokerage measures
  gbr<-apply(br,2,sum)
  #Calculate expectations and such
  d<-m/(N*(N-1))
  clid<-unique(cl)         #Count the class memberships
  n<-vector()
  for(i in clid)
    n<-c(n,sum(cl==i))
  n<-as.double(n)          #This shouldn't be needed, but R will generate
  N<-as.double(N)          #integer overflows unless we coerce to double!
  ebr<-matrix(0,length(clid),6)
  vbr<-matrix(0,length(clid),6)
  for(i in 1:length(clid)){  #Compute moments by broker's class
    #Type 1: Within-group (wI)
    ebr[i,1]<-d^2*(1-d)*(n[i]-1)*(n[i]-2)
    vbr[i,1]<-ebr[i,1]*(1-d^2*(1-d))+2*(n[i]-1)*(n[i]-2)*(n[i]-3)*d^3*(1-d)^3
    #Type 2: Itinerant (WO)
    ebr[i,2]<-d^2*(1-d)*sum(n[-i]*(n[-i]-1))
    vbr[i,2]<-ebr[i,2]*(1-d^2*(1-d))+ 2*sum(n[-i]*(n[-i]-1)*(n[-i]-2))*d^3*(1-d)^3
    #Type 3: Representative (bIO)
    ebr[i,3]<-d^2*(1-d)*(N-n[i])*(n[i]-1)
    vbr[i,3]<-ebr[i,3]*(1-d^2*(1-d))+ 2*((n[i]-1)*choose(N-n[i],2)+(N-n[i])*choose(n[i]-1,2))*d^3*(1-d)^3
    #Type 4: Gatekeeping (bOI)
    ebr[i,4]<-ebr[i,3]
    vbr[i,4]<-vbr[i,3]
    #Type 5: Liason (bO)
    ebr[i,5]<-d^2*(1-d)*(sum((n[-i])%o%(n[-i]))-sum(n[-i]^2))
    vbr[i,5]<-ebr[i,5]*(1-d^2*(1-d))+ 4*sum(n[-i]*choose(N-n[-i]-n[i],2)*d^3*(1-d)^3)
    #Total
    ebr[i,6]<-d^2*(1-d)*(N-1)*(N-2)
    vbr[i,6]<-ebr[i,6]*(1-d^2*(1-d))+2*(N-1)*(N-2)*(N-3)*d^3*(1-d)^3
  }
  br.exp<-vector()
  br.sd<-vector()
  for(i in 1:N){
    temp<-match(cl[i],clid)
    br.exp<-rbind(br.exp,ebr[temp,])
    br.sd<-rbind(br.sd,sqrt(vbr[temp,]))
  }
  br.z<-(br-br.exp)/br.sd
  egbr<-vector()                     #Global expections/variances
  vgbr<-vector()
  #Type 1: Within-group (wI)
  egbr[1]<-d^2*(1-d)*sum(n*(n-1)*(n-2))
  vgbr[1]<-egbr[1]*(1-d^2*(1-d))+ sum(n*(n-1)*(n-2)*(((4*n-10)*d^3*(1-d)^3)-(4*(n-3)*d^4*(1-d)^2)+((n-3)*d^5*(1-d))))
  #Type 2: Itinerant (WO)
  egbr[2]<-d^2*(1-d)*sum(n*(N-n)*(n-1))
  vgbr[2]<-egbr[2]*(1-d^2*(1-d))+ (sum(outer(n,n,function(x,y){x*y*(x-1)*(((2*x+2*y-6)*d^3*(1-d)^3)+((N-x-1)*d^5*(1-d)))})) - sum(n*n*(n-1)*(((4*n-6)*d^3*(1-d)^3)+((N-n-1)*d^5*(1-d)))))
  #Type 3: Representative (bIO)
  egbr[3]<-d^2*(1-d)*sum(n*(N-n)*(n-1))
  vgbr[3]<-egbr[3]*(1-d^2*(1-d))+ sum(n*(N-n)*(n-1)*(((N-3)*d^3*(1-d)^3)+((n-2)*d^5*(1-d))))
  #Type 4: Gatekeeping (bOI)
  egbr[4]<-egbr[3]
  vgbr[4]<-vgbr[3]
  #Type 5: Liason (bO)
  egbr[5]<- d^2*(1-d)*(sum(outer(n,n,function(x,y){x*y*(N-x-y)}))-sum(n*n*(N-2*n)))
  vgbr[5]<-egbr[5]*(1-d^2*(1-d))
  for(i in 1:length(n))
    for(j in 1:length(n))
      for(k in 1:length(n))
        if((i!=j)&&(j!=k)&&(i!=k))
          vgbr[5]<-vgbr[5] + n[i]*n[j]*n[k] * ((4*(N-n[j])-2*(n[i]+n[k]+1))*d^3*(1-d)^3-(4*(N-n[k])-2*(n[i]+n[j]+1))*d^4*(1-d)^2+(N-(n[i]+n[k]+1))*d^5*(1-d))
  #Total
  egbr[6]<-d^2*(1-d)*N*(N-1)*(N-2)
  vgbr[6]<-egbr[6]*(1-d^2*(1-d))+ N*(N-1)*(N-2)*(((4*N-10)*d^3*(1-d)^3)-(4*(N-3)*d^4*(1-d)^2)+((N-3)*d^5*(1-d)))
  
  #Return the results
  br.nam<-c("w_I","w_O","b_IO","b_OI","b_O","t")
  colnames(br)<-br.nam
  rownames(br)<-attr(g,"vnames")
  colnames(br.exp)<-br.nam
  rownames(br.exp)<-attr(g,"vnames")
  colnames(br.sd)<-br.nam
  rownames(br.sd)<-attr(g,"vnames")
  colnames(br.z)<-br.nam
  rownames(br.z)<-attr(g,"vnames")
  names(gbr)<-br.nam
  names(egbr)<-br.nam
  names(vgbr)<-br.nam
  colnames(ebr)<-br.nam
  rownames(ebr)<-clid
  colnames(vbr)<-br.nam
  rownames(vbr)<-clid
  out<-list(raw.nli=br,exp.nli=br.exp,sd.nli=br.sd,z.nli=br.z,raw.gli=gbr, exp.gli=egbr,sd.gli=sqrt(vgbr),z.gli=(gbr-egbr)/sqrt(vgbr),exp.grp=ebr, sd.grp=sqrt(vbr),cl=cl,clid=clid,n=n,N=N)
  class(out)<-"brokerage"
  out
}


#coef.bn - Coefficient method for bn
coef.bn<-function(object, ...){
  coef<-c(object$d,object$pi,object$sigma,object$rho)
  names(coef)<-c("d","pi","sigma","rho")
  coef
}


#coef.lnam - Coefficient method for lnam
coef.lnam<-function(object, ...){
   coefs<-vector()
#   cn<-vector()
   if(!is.null(object$beta)){
      coefs<-c(coefs,object$beta)
#      cn<-c(cn,names(object$beta))
   }
   if(!is.null(object$rho1)){
      coefs<-c(coefs,object$rho1)
#      cn<-c(cn,"rho1")
   }
   if(!is.null(object$rho2)){
      coefs<-c(coefs,object$rho2)
#      cn<-c(cn,"rho2")
   }
#   names(coefs)<-cn
   coefs
}


#consensus - Find a consensus structure, using one of several algorithms.  Note 
#that this is currently experimental, and that the routines are not guaranteed 
#to produce meaningful output
consensus<-function(dat,mode="digraph",diag=FALSE,method="central.graph",tol=1e-6,maxiter=1e3,verbose=TRUE,no.bias=FALSE){
   #First, prepare the data
   dat<-as.sociomatrix.sna(dat)
   if(is.list(dat))
     stop("consensus requires graphs of identical order.")
   if(is.matrix(dat))
     m<-1
   else
   m<-dim(dat)[1]
   n<-dim(dat)[2]
   if(m==1)
     dat<-array(dat,dim=c(1,n,n))
   if(mode=="graph")
      d<-upper.tri.remove(dat)
   else
      d<-dat
   if(!diag)
      d<-diag.remove(d)
   #Now proceed by method
   #First, use the central graph if called for
   if(method=="central.graph"){
      cong<-centralgraph(d)
   #Try the iterative reweighting algorithm....
   }else if(method=="iterative.reweight"){
      cong<-centralgraph(d)
      ans<-sweep(d,c(2,3),cong,"==")
      comp<-pmax(apply(ans,1,mean,na.rm=TRUE),0.5)
      if(no.bias)
        bias<-rep(0.5,length(comp))
      else
        bias<-apply(sweep(d,c(2,3),!ans,"*"),1,mean,na.rm=TRUE)
      cdiff<-1+tol
      iter<-1
      while((cdiff>tol)&&(iter<maxiter)){
        ll1<-apply(sweep(d,1,log(comp+(1-comp)*bias),"*") + sweep(1-d,1,log((1-comp)*(1-bias)),"*"), c(2,3), sum, na.rm=TRUE)
        ll0<-apply(sweep(1-d,1,log(comp+(1-comp)*(1-bias)),"*") + sweep(d,1,log((1-comp)*bias),"*"), c(2,3), sum, na.rm=TRUE)
        cong<-ll1>ll0
        ans<-sweep(d,c(2,3),cong,"==")
        ocomp<-comp
        comp<-pmax(apply(ans,1,mean,na.rm=TRUE),0.5)
        bias<-apply(sweep(d,c(2,3),!ans,"*"),1,mean,na.rm=TRUE)
        cdiff<-sum(abs(ocomp-comp))
        iter<-iter+1
      }
      if(verbose){
        cat("Estimated competency scores:\n")
        print(comp)
        cat("Estimated bias parameters:\n")
        print(bias)
      }
   #Perform a single reweighting using mean correlation
   }else if(method=="single.reweight"){
      gc<-gcor(d)
      gc[is.na(gc)]<-0
      diag(gc)<-1
      rwv<-apply(gc,1,sum)
      rwv<-rwv/sum(rwv)
      cong<-apply(d*aperm(array(sapply(rwv,rep,n^2),dim=c(n,n,m)),c(3,2,1)),c(2,3),sum)
   #Perform a single reweighting using first component loadings
   }else if(method=="PCA.reweight"){
      gc<-gcor(d)
      gc[is.na(gc)]<-0
      diag(gc)<-1
      rwv<-abs(eigen(gc)$vector[,1])
      cong<-apply(d*aperm(array(sapply(rwv,rep,n^2),dim=c(n,n,m)), c(3,2,1)),c(2,3),sum)
   #Use the (proper) Romney-Batchelder model
   }else if(method=="romney.batchelder"){
     d<-d[!apply(is.na(d),1,all),,]              #Remove any missing informants
     if(length(dim(d))<3)
       stop("Insufficient informant information.")
     #Create the initial estimates
     drate<-apply(d,1,mean,na.rm=TRUE)
     cong<-apply(d,c(2,3),mean,na.rm=TRUE)>0.5   #Estimate graph
     s1<-mean(cong,na.rm=TRUE)
     s0<-mean(1-cong,na.rm=TRUE)
     correct<-sweep(d,c(2,3),cong,"==")          #Check for correctness
     correct<-apply(correct,1,mean,na.rm=TRUE)
     comp<-pmax(2*correct-1,0)                   #Estimate competencies
     if(no.bias)
       bias<-rep(0.5,length(comp))
     else{
       bias<-pmin(pmax((drate-s1*comp)/(1-comp),0),1)
       bias[comp==1]<-0.5
     }
     #Now, iterate until the system converges
     ocomp<-comp+tol+1
     iter<-1
     while((max(abs(ocomp-comp),na.rm=TRUE)>tol)&&(iter<maxiter)){
       ocomp<-comp
       #Estimate cong, based on the local MLE
       ll1<-apply(sweep(d,1,log(comp+(1-comp)*bias),"*") + sweep(1-d,1,log((1-comp)*(1-bias)),"*"), c(2,3), sum, na.rm=TRUE)
       ll0<-apply(sweep(1-d,1,log(comp+(1-comp)*(1-bias)),"*") + sweep(d,1,log((1-comp)*bias),"*"), c(2,3), sum, na.rm=TRUE)
       cong<-ll1>ll0
       s1<-mean(cong,na.rm=TRUE)
       s0<-mean(1-cong,na.rm=TRUE)
       #Estimate competencies, using EM
       correct<-sweep(d,c(2,3),cong,"==")         #Check for correctness
       correct<-apply(correct,1,mean,na.rm=TRUE)
       comp<-pmax((correct-s1*bias-s0*(1-bias))/(1-s1*bias-s0*(1-bias)),0)
       #Estimate bias (if no.bias==FALSE), using EM
       if(!no.bias)
         bias<-pmin(pmax((drate-s1*comp)/(1-comp),0),1)
     }
     #Possibly, dump fun messages
     if(verbose){
       cat("Estimated competency scores:\n")
       print(comp)
       cat("Estimated bias parameters:\n")
       print(bias)
     }
   #Use the Locally Aggregated Structure
   }else if(method=="LAS.intersection"){
      cong<-matrix(0,n,n)
      for(i in 1:n)
        for(j in 1:n)
          cong[i,j]<-as.numeric(d[i,i,j]&&d[j,i,j])
   }else if(method=="LAS.union"){
      cong<-matrix(0,n,n)
      for(i in 1:n)
        for(j in 1:n)
          cong[i,j]<-as.numeric(d[i,i,j]||d[j,i,j])
   }else if(method=="OR.row"){
      cong<-matrix(0,n,n)
      for(i in 1:n)
         cong[i,]<-d[i,i,]
   }else if(method=="OR.col"){
      cong<-matrix(0,n,n)
      for(i in 1:n)
         cong[,i]<-d[i,,i]
   }
   #Finish off and return the consensus graph
   if(mode=="graph")
      cong[upper.tri(cong)]<-t(cong)[upper.tri(cong)]
   if(!diag)
      diag(cong)<-0
   cong
}


#eval.edgeperturbation - Evaluate a function on a given graph with and without a
#given edge, returning the difference between the results in each case.
eval.edgeperturbation<-function(dat,i,j,FUN,...){
   #Get the function in question
   fun<-match.fun(FUN)
   #Set up the perturbation matrices
   present<-dat
   present[i,j]<-1
   absent<-dat
   absent[i,j]<-0
   #Evaluate the function across the perturbation and return the difference
   fun(present,...)-fun(absent,...)
}


#lnam - Fit a linear network autocorrelation model
#y = r1 * W1 %*% y + X %*% b + e, e = r2 * W2 %*% e + nu
#y =  (I-r1*W1)^-1%*%(X %*% b + e)
#y = (I-r1 W1)^-1 (X %*% b + (I-r2 W2)^-1 nu)
#e = (I-r2 W2)^-1 nu
#e = (I-r1 W1) y - X b
#nu = (I - r2 W2) [ (I-r1 W1) y - X b ]
#nu = (I-r2 W2) e
lnam<-function(y,x=NULL,W1=NULL,W2=NULL,theta.seed=NULL,null.model=c("meanstd","mean","std","none"),method="BFGS",control=list(),tol=1e-10){
   #Define the log-likelihood functions for each case
   agg<-function(a,w){
     m<-length(w)
     n<-dim(a)[2]
     mat<-as.double(matrix(0,n,n))
     matrix(.C("aggarray3d_R",as.double(a),as.double(w),mat=mat,as.integer(m), as.integer(n),PACKAGE="sna",NAOK=TRUE)$mat,n,n)
   }
  #Estimate covariate effects, conditional on autocorrelation parameters
  betahat<-function(y,X,W1a,W2a){
    if(nw1==0){
      if(nw2==0){
        return(qr.solve(t(X)%*%X,t(X)%*%y))
      }else{
        tXtW2aW2a<-t(X)%*%t(W2a)%*%W2a
        return(qr.solve(tXtW2aW2a%*%X,tXtW2aW2a%*%y))
      }
    }else{
      if(nw2==0){
        return(qr.solve(t(X)%*%X,t(X)%*%W1a%*%y))
      }else{
        tXtW2aW2a<-t(X)%*%t(W2a)%*%W2a
        qr.solve(tXtW2aW2a%*%X,tXtW2aW2a%*%W1a%*%y)
      }
    }
  }
  #Estimate predicted means, conditional on other effects
  muhat<-function(y,X,W1a,W2a,betahat){
    if(nx>0)
      Xb<-X%*%betahat
    else
      Xb<-0
    switch((nw1>0)+2*(nw2>0)+1,
      y-Xb,
      W1a%*%y-Xb,
      W2a%*%(y-Xb),
      W2a%*%(W1a%*%y-Xb)
    )
  }
  #Estimate innovation variance, conditional on other effects
  sigmasqhat<-function(muhat){
    t(muhat)%*%muhat/length(muhat)
  }
  #Model deviance (for use with fitting rho | beta, sigma)
  n2ll.rho<-function(rho,beta,sigmasq){
    #Prepare ll elements according to which parameters are present
    if(nw1>0){
      W1a<-diag(n)-agg(W1,rho[1:nw1])
      W1ay<-W1a%*%y
      adetW1a<-abs(det(W1a))
    }else{
      W1ay<-y
      adetW1a<-1
    }
    if(nw2>0){
      W2a<-diag(n)-agg(W2,rho[(nw1+1):(nw1+nw2)])
      tpW2a<-t(W2a)%*%W2a
      adetW2a<-abs(det(W2a))
    }else{
      tpW2a<-diag(n)
      adetW2a<-1
    }
    if(nx>0){
      Xb<-x%*%beta
    }else{
      Xb<-0
    }
    #Compute and return
    n*(log(2*pi)+log(sigmasq)) + t(W1ay-Xb)%*%tpW2a%*%(W1ay-Xb)/sigmasq - 2*(log(adetW1a)+log(adetW2a))
  }
  #Model deviance (general purpose)
  n2ll<-function(W1a,W2a,sigmasqhat){
    switch((nw1>0)+2*(nw2>0)+1,
      n*(1+log(2*pi)+log(sigmasqhat)),
      n*(1+log(2*pi)+log(sigmasqhat))-2*log(abs(det(W1a))),
      n*(1+log(2*pi)+log(sigmasqhat))-2*log(abs(det(W2a))),
      n*(1+log(2*pi)+log(sigmasqhat))- 2*(log(abs(det(W1a)))+log(abs(det(W2a))))
    )
  }
  #Conduct a single iterative refinement of a set of initial parameter estimates
  estimate<-function(parm,final=FALSE){
    #Either aggregate the weight matrices, or NULL them
    if(nw1>0)
      W1a<-diag(n)-agg(W1,parm$rho1)
    else
      W1a<-NULL
    if(nw2>0)
      W2a<-diag(n)-agg(W2,parm$rho2)
    else
      W2a<-NULL
    #If covariates were given, estimate beta | rho
    if(nx>0)
      parm$beta<-betahat(y,x,W1a,W2a)
    #Estimate sigma | beta, rho
    parm$sigmasq<-sigmasqhat(muhat(y,x,W1a,W2a,parm$beta))
    #If networks were given, (and not final) estimate rho | beta, sigma
    if(!(final||(nw1+nw2==0))){
      rho<-c(parm$rho1,parm$rho2)
      temp<-optim(rho,n2ll.rho,method=method,control=control,beta=parm$beta, sigmasq=parm$sigmasq)
      if(nw1>0)
        parm$rho1<-temp$par[1:nw1]
      if(nw2>0)
        parm$rho2<-temp$par[(nw1+1):(nw1+nw2)]
    }
    #Calculate model deviance
    parm$dev<-n2ll(W1a,W2a,parm$sigmasq)
    #Return the parameter list
    parm
  }
  #Calculate the expected Fisher information matrix for a fitted model
  infomat<-function(parm){     #Numerical version (requires numDeriv)
    requireNamespace('numDeriv')
    locnll<-function(par){
      #Prepare ll elements according to which parameters are present
      if(nw1>0){
        W1a<-diag(n)-agg(W1,par[(nx+1):(nx+nw1)])
        W1ay<-W1a%*%y
        ladetW1a<-log(abs(det(W1a)))
      }else{
        W1ay<-y
        ladetW1a<-0
      }
      if(nw2>0){
        W2a<-diag(n)-agg(W2,par[(nx+nw1+1):(nx+nw1+nw2)])
        tpW2a<-t(W2a)%*%W2a
        ladetW2a<-log(abs(det(W2a)))
      }else{
        tpW2a<-diag(n)
        ladetW2a<-0
      }
      if(nx>0){
        Xb<-x%*%par[1:nx]
      }else{
        Xb<-0
      }
      #Compute and return
      n/2*(log(2*pi)+log(par[m]))+ t(W1ay-Xb)%*%tpW2a%*%(W1ay-Xb)/(2*par[m]) -ladetW1a-ladetW2a
    }
    #Return the information matrix
    numDeriv::hessian(locnll,c(parm$beta,parm$rho1,parm$rho2,parm$sigmasq)) 
  }
  #How many data points are there?
  n<-length(y)
  #Fix x, W1, and W2, if needed, and count predictors
  if(!is.null(x)){
    if(is.vector(x))
      x<-as.matrix(x)
    if(NROW(x)!=n)
      stop("Number of observations in x must match length of y.")
    nx<-NCOL(x)
  }else
    nx<-0
  if(!is.null(W1)){
    W1<-as.sociomatrix.sna(W1)
    if(!(is.matrix(W1)||is.array(W1)))
      stop("All networks supplied in W1 must be of identical order.")
    if(dim(W1)[2]!=n)
      stop("Order of W1 must match length of y.")
    if(length(dim(W1))==2)
      W1<-array(W1,dim=c(1,n,n))
    nw1<-dim(W1)[1]
  }else
    nw1<-0
  if(!is.null(W2)){
    W2<-as.sociomatrix.sna(W2)
    if(!(is.matrix(W2)||is.array(W2)))
      stop("All networks supplied in W2 must be of identical order.")
    if(dim(W2)[2]!=n)
      stop("Order of W2 must match length of y.")
    if(length(dim(W2))==2)
      W2<-array(W2,dim=c(1,n,n))
    nw2<-dim(W2)[1]
  }else
    nw2<-0
   #Determine the computation mode from the x,W1,W2 parameters
   comp.mode<-as.character(as.numeric(1*(nx>0)+10*(nw1>0)+100*(nw2>0)))
   if(comp.mode=="0")
      stop("At least one of x, W1, W2 must be specified.\n")
   #How many predictors?   
   m<-switch(comp.mode,
      "1"=nx+1,
      "10"=nw1+1,
      "100"=nw2+1,
      "11"=nx+nw1+1,
      "101"=nx+nw2+1,
      "110"=nw1+nw2+1,
      "111"=nx+nw1+nw2+1
   )
  #Initialize the parameter list
  parm<-list()
  if(is.null(theta.seed)){
    if(nx>0)
      parm$beta<-rep(0,nx)
    if(nw1>0)
      parm$rho1<-rep(0,nw1)
    if(nw2>0)
      parm$rho2<-rep(0,nw2)
    parm$sigmasq<-1
  }else{
    if(nx>0)
      parm$beta<-theta.seed[1:nx]
    if(nw1>0)
      parm$rho1<-theta.seed[(nx+1):(nx+nw1)]
    if(nw2>0)
      parm$rho2<-theta.seed[(nx+nw1+1):(nx+nw1+nw2)]
    parm$sigmasq<-theta.seed[nx+nw1+nw2+1]
  }
  parm$dev<-Inf
  #Fit the model
  olddev<-Inf
  while(is.na(parm$dev-olddev)||(abs(parm$dev-olddev)>tol)){
    olddev<-parm$dev
    parm<-estimate(parm,final=FALSE)
  }
  parm<-estimate(parm,final=TRUE)  #Final refinement
  #Assemble the result
  o<-list()
  o$y<-y
  o$x<-x
  o$W1<-W1
  o$W2<-W2
  o$model<-comp.mode
  o$infomat<-infomat(parm)
  o$acvm<-qr.solve(o$infomat)
  o$null.model<-match.arg(null.model)
  o$lnlik.null<-switch(match.arg(null.model),  #Fit a null model
    "meanstd"=sum(dnorm(y-mean(y),0,as.numeric(sqrt(var(y))),log=TRUE)),
    "mean"=sum(dnorm(y-mean(y),log=TRUE)),
    "std"=sum(dnorm(y,0,as.numeric(sqrt(var(y))),log=TRUE)),
    "none"=sum(dnorm(y,log=TRUE))
  )
  o$df.null.resid<-switch(match.arg(null.model),  #Find residual null df
    "meanstd"=n-2,
    "mean"=n-1,
    "std"=n-1,
    "none"=n
  )
  o$df.null<-switch(match.arg(null.model),  #Find null df
    "meanstd"=2,
    "mean"=1,
    "std"=1,
    "none"=0
  )
  o$null.param<-switch(match.arg(null.model),  #Find null params, if any
    "meanstd"=c(mean(y),sqrt(var(y))),
    "mean"=mean(y),
    "std"=sqrt(var(y)),
    "none"=NULL
  )
  o$lnlik.model<--parm$dev/2
  o$df.model<-m
  o$df.residual<-n-m
  o$df.total<-n
  o$beta<-parm$beta                       #Extract parameters
  o$rho1<-parm$rho1  
  o$rho2<-parm$rho2  
  o$sigmasq<-parm$sigmasq
  o$sigma<-o$sigmasq^0.5
  temp<-sqrt(diag(o$acvm))                #Get standard errors
  if(nx>0)
    o$beta.se<-temp[1:nx]
  if(nw1>0)
    o$rho1.se<-temp[(nx+1):(nx+nw1)]
  if(nw2>0)
    o$rho2.se<-temp[(nx+nw1+1):(nx+nw1+nw2)]
  o$sigmasq.se<-temp[m]
  o$sigma.se<-o$sigmasq.se^2/(4*o$sigmasq)  #This a delta method approximation
  if(!is.null(o$beta)){                   #Set X names
    if(!is.null(colnames(x))){
       names(o$beta)<-colnames(x)
       names(o$beta.se)<-colnames(x)
    }else{
       names(o$beta)<-paste("X",1:nx,sep="")
       names(o$beta.se)<-paste("X",1:nx,sep="")
    }
  }
  if(!is.null(o$rho1)){                     #Set W1 names
    if((!is.null(dimnames(W1)))&&(!is.null(dimnames(W1)[[1]]))){
       names(o$rho1)<-dimnames(W1)[[1]]
       names(o$rho1.se)<-dimnames(W1)[[1]]
    }else{
       names(o$rho1)<-paste("rho1",1:nw1,sep=".")
       names(o$rho1.se)<-paste("rho1",1:nw1,sep=".")
    }
  }
  if(!is.null(o$rho2)){                     #Set W2 names
    if((!is.null(dimnames(W2)))&&(!is.null(dimnames(W2)[[1]]))){
       names(o$rho2)<-dimnames(W2)[[1]]
       names(o$rho2.se)<-dimnames(W2)[[1]]
    }else{
       names(o$rho2)<-paste("rho2",1:nw2,sep=".")
       names(o$rho2.se)<-paste("rho2",1:nw2,sep=".")
    }
  }
  if(nw1>0)                               #Aggregate W1 weights
     W1ag<-agg(W1,o$rho1)
  if(nw2>0)                               #Aggregate W2 weights
     W2ag<-agg(W2,o$rho2)
  o$disturbances<-as.vector(switch(comp.mode,  #The estimated disturbances
    "1"=y-x%*%o$beta,
    "10"=(diag(n)-W1ag)%*%y,
    "100"=(diag(n)-W2ag)%*%y,
    "11"=(diag(n)-W1ag)%*%y-x%*%o$beta,
    "101"=(diag(n)-W2ag)%*%(y-x%*%o$beta),
    "110"=(diag(n)-W2ag)%*%((diag(n)-W1ag)%*%y),
    "111"=(diag(n)-W2ag)%*%((diag(n)-W1ag)%*%y-x%*%o$beta)
  ))
  o$fitted.values<-as.vector(switch(comp.mode,  #Compute the fitted values
    "1"=x%*%o$beta,
    "10"=rep(0,n),
    "100"=rep(0,n),
    "11"=qr.solve(diag(n)-W1ag,x%*%o$beta),
    "101"=x%*%o$beta,
    "110"=rep(0,n),
    "111"=qr.solve(diag(n)-W1ag,x%*%o$beta)
  ))
  o$residuals<-as.vector(y-o$fitted.values)
  o$call<-match.call()
  class(o)<-c("lnam")
  o
}


#nacf - Network autocorrelation function
nacf<-function(net,y,lag.max=NULL,type=c("correlation","covariance","moran","geary"),neighborhood.type=c("in","out","total"),partial.neighborhood=TRUE,mode="digraph",diag=FALSE,thresh=0,demean=TRUE){
  #Pre-process the raw input
  net<-as.sociomatrix.sna(net)
  if(is.list(net))
    return(lapply(net,nacf,y=y,lag.max=lag.max, neighborhood.type=neighborhood.type,partial.neighborhood=partial.neighborhood,mode=mode,diag=diag,thresh=thresh,demean=demean))
  else if(length(dim(net))>2)
    return(apply(net,1,nacf,y=y,lag.max=lag.max, neighborhood.type=neighborhood.type,partial.neighborhood=partial.neighborhood,mode=mode,diag=diag,thresh=thresh,demean=demean))
  #End pre-processing
  if(length(y)!=NROW(net))
    stop("Network size must match covariate length in nacf.")
  #Process y
  if(demean||(match.arg(type)=="moran"))
    y<-y-mean(y)
  vary<-var(y)
  #Determine maximum lag, if needed
  if(is.null(lag.max))
    lag.max<-NROW(net)-1
  #Get the appropriate neighborhood graphs for dat
  neigh<-neighborhood(net,order=lag.max,neighborhood.type=neighborhood.type, mode=mode,diag=diag,thresh=thresh,return.all=TRUE,partial=partial.neighborhood)
  #Form the coefficients
  v<-switch(match.arg(type),
    "covariance"=t(y)%*%y/NROW(net),
    "correlation"=1,
    "moran"=1,
    "geary"=0,
  )
  for(i in 1:lag.max){
    ec<-sum(neigh[i,,])
    if(ec>0){
      v[i+1]<-switch(match.arg(type),
        "covariance"=(t(y)%*%neigh[i,,]%*%y)/ec,
        "correlation"=((t(y)%*%neigh[i,,]%*%y)/ec)/vary,
        "moran"=NROW(net)/ec*sum((y%o%y)*neigh[i,,])/sum(y^2),
        "geary"=(NROW(net)-1)/(2*ec)*sum(neigh[i,,]*outer(y,y,"-")^2)/ sum((y-mean(y))^2),
      )
    }else
      v[i+1]<-0
  }
  names(v)<-0:lag.max
  #Return the results
  v
}


#netcancor - Canonical correlations for network variables. 
netcancor<-function(y,x,mode="digraph",diag=FALSE,nullhyp="cugtie",reps=1000){
   y<-as.sociomatrix.sna(y)
   x<-as.sociomatrix.sna(x)
   if(is.list(x)|is.list(y))
     stop("netcancor requires graphs of identical order.")
   if(length(dim(y))>2){
      iy<-matrix(nrow=dim(y)[1],ncol=dim(y)[2]*dim(y)[3])
   }else{
      iy<-matrix(nrow=1,ncol=dim(y)[1]*dim(y)[2])
      temp<-y
      y<-array(dim=c(1,dim(temp)[1],dim(temp)[2]))
      y[1,,]<-temp
   }
   if(length(dim(x))>2){
      ix<-matrix(nrow=dim(x)[1],ncol=dim(x)[2]*dim(x)[3])
   }else{
      ix<-matrix(nrow=1,ncol=dim(x)[1]*dim(x)[2])
      temp<-x
      x<-array(dim=c(1,dim(temp)[1],dim(temp)[2]))
      x[1,,]<-temp
   }
   my<-dim(y)[1]
   mx<-dim(x)[1]
   n<-dim(y)[2]
   out<-list()
   out$xdist<-array(dim=c(reps,mx,mx))
   out$ydist<-array(dim=c(reps,my,my))
   #Convert the response first.
   for(i in 1:my){
      d<-y[i,,]
      #if(!diag){
      #   diag(d)<-NA
      #}
      #if(mode!="digraph")
      #   d[lower.tri(d)]<-NA
      iy[i,]<-as.vector(d)
   }
   #Now for the independent variables.
   for(i in 1:mx){
      d<-x[i,,]
      #if(!diag){
      #   diag(d)<-NA
      #}
      #if(mode!="digraph")
      #   d[lower.tri(d)]<-NA
      ix[i,]<-as.vector(d)
   }   
   #Run the initial model fit
   nc<-cancor(t(ix),t(iy))  #Had to take out na.action=na.omit, since it's not supported
   #Now, repeat the whole thing an ungodly number of times.
   out$cdist<-array(dim=c(reps,length(nc$cor)))
   for(i in 1:reps){
      #Clear out the internal structures
      iy<-matrix(nrow=dim(y)[1],ncol=dim(y)[2]*dim(y)[3])
      ix<-matrix(nrow=dim(x)[1],ncol=dim(x)[2]*dim(x)[3])
      #Convert (and mutate) the response first.
      for(j in 1:my){
         d<-switch(nullhyp,
            qap = rmperm(y[j,,]),
            cug = rgraph(n,1,mode=mode,diag=diag),
            cugden = rgraph(n,1,tprob=gden(y[j,,],mode=mode,diag=diag),mode=mode,diag=diag),
            cugtie = rgraph(n,1,mode=mode,diag=diag,tielist=y[j,,])
         )
         #if(!diag){
         #   diag(d)<-NA
         #}
         #if(mode!="digraph")
         #   d[lower.tri(d)]<-NA
         iy[j,]<-as.vector(d)
      }
      #Now for the independent variables.
      for(j in 1:mx){
         d<-switch(nullhyp,
            qap = rmperm(x[j,,]),
            cug = rgraph(n,1,mode=mode,diag=diag),
            cugden = rgraph(n,1,tprob=gden(x[j,,],mode=mode,diag=diag),mode=mode,diag=diag),
            cugtie = rgraph(n,1,mode=mode,diag=diag,tielist=x[j,,])
         )
         #if(!diag){
         #   diag(d)<-NA
         #}
         #if(mode!="digraph")
         #   d[lower.tri(d)]<-NA
         ix[j,]<-as.vector(d)
      }   
      #Finally, fit the test model
      tc<-cancor(t(ix),t(iy))         #Had to take out na.action=na.omit, since it's not supported
      #Gather the coefficients for use later...
      out$cdist[i,]<-tc$cor
      out$xdist[i,,]<-tc$xcoef
      out$ydist[i,,]<-tc$ycoef
   }
   #Find the p-values for our monte carlo null hypothesis tests
   out$cor<-nc$cor
   out$xcoef<-nc$xcoef
   out$ycoef<-nc$ycoef
   out$cpgreq<-vector(length=length(nc$cor))
   out$cpleeq<-vector(length=length(nc$cor))
   for(i in 1:length(nc$cor)){
      out$cpgreq[i]<-mean(out$cdist[,i]>=out$cor[i],na.rm=TRUE)
      out$cpleeq[i]<-mean(out$cdist[,i]<=out$cor[i],na.rm=TRUE)
   }
   out$xpgreq<-matrix(ncol=mx,nrow=mx)
   out$xpleeq<-matrix(ncol=mx,nrow=mx)
   for(i in 1:mx){
      for(j in 1:mx){ 
         out$xpgreq[i,j]<-mean(out$xdist[,i,j]>=out$xcoef[i,j],na.rm=TRUE)
         out$xpleeq[i,j]<-mean(out$xdist[,i,j]<=out$xcoef[i,j],na.rm=TRUE)
      }
   }
   out$ypgreq<-matrix(ncol=my,nrow=my)
   out$ypleeq<-matrix(ncol=my,nrow=my)
   for(i in 1:my){
      for(j in 1:my){ 
         out$ypgreq[i,j]<-mean(out$ydist[,i,j]>=out$ycoef[i,j],na.rm=TRUE)
         out$ypleeq[i,j]<-mean(out$ydist[,i,j]<=out$ycoef[i,j],na.rm=TRUE)
      }
   }
   #Having completed the model fit and MC tests, we gather useful information for
   #the end user.  This is a combination of cancor output and our own stuff.
   out$cnames<-as.vector(paste("cor",1:min(mx,my),sep=""))
   out$xnames<-as.vector(paste("x",1:mx,sep=""))
   out$ynames<-as.vector(paste("y",1:my,sep=""))
   out$xcenter<-nc$xcenter
   out$ycenter<-nc$ycenter
   out$nullhyp<-nullhyp
   class(out)<-c("netcancor")
   out
}


#netlm - OLS network regrssion routine (w/many null hypotheses)
#  #QAP semi-partialling "plus" (Dekker et al.)
#  #For Y ~ b0 + b1 X1 + b2 X2 + ... + bp Xp
#  #for(i in 1:p)
#  #  Fit Xi ~ b0* + b1* X1 + ... + bp* Xp (omit Xi)
#  #  Let ei = resid of above lm
#  #  for(j in 1:reps)
#  #    eij = rmperm (ei)
#  #    Fit Y ~ b0** + b1** X1 + ... + bi** eij + ... + bp** Xp
#  #Use resulting permutation distributions to test coefficients
netlm<-function(y,x,intercept=TRUE,mode="digraph",diag=FALSE,nullhyp=c("qap", "qapspp","qapy","qapx","qapallx","cugtie","cugden","cuguman","classical"),test.statistic=c("t-value","beta"),tol=1e-7, reps=1000){
  #Define an internal routine to perform a QR reduction and get t-values
  gettval<-function(x,y,tol){
    xqr<-qr(x,tol=tol)
    coef<-qr.coef(xqr,y)
    resid<-qr.resid(xqr,y)
    rank<-xqr$rank
    n<-length(y)
    rdf<-n-rank
    resvar<-sum(resid^2)/rdf
    cvm<-chol2inv(xqr$qr)
    se<-sqrt(diag(cvm)*resvar)
    coef/se
  }
  #Define an internal routine to quickly fit linear models to graphs
  gfit<-function(glist,mode,diag,tol,rety,tstat){
    y<-gvectorize(glist[[1]],mode=mode,diag=diag,censor.as.na=TRUE)
    x<-vector()
    for(i in 2:length(glist))
      x<-cbind(x,gvectorize(glist[[i]],mode=mode,diag=diag,censor.as.na=TRUE))
    if(!is.matrix(x))
      x<-matrix(x,ncol=1)
    mis<-is.na(y)|apply(is.na(x),1,any)
    if(!rety){
      if(tstat=="beta")
        qr.solve(x[!mis,],y[!mis],tol=tol)
      else if(tstat=="t-value"){
        gettval(x[!mis,],y[!mis],tol=tol)
      }
    }else{
      list(qr(x[!mis,],tol=tol),y[!mis])
    }
  }
  #Get the data in order
  y<-as.sociomatrix.sna(y)
  x<-as.sociomatrix.sna(x)
  if(is.list(y)||((length(dim(y))>2)&&(dim(y)[1]>1))) 
    stop("y must be a single graph in netlm.")
  if(length(dim(y))>2)
    y<-y[1,,]
  if(is.list(x)||(dim(x)[2]!=dim(y)[2]))
    stop("Homogeneous graph orders required in netlm.")
  nx<-stackcount(x)+intercept    #Get number of predictors
  n<-dim(y)[2]                   #Get graph order
  g<-list(y)                     #Put graphs into a list
  if(intercept)
    g[[2]]<-matrix(1,n,n)
  if(nx-intercept==1)
    g[[2+intercept]]<-x
  else
    for(i in 1:(nx-intercept))
      g[[i+1+intercept]]<-x[i,,]
  if(any(sapply(lapply(g,is.na),any)))
    warning("Missing data supplied to netlm; this may pose problems for certain null hypotheses.  Hope you know what you're doing....")
  #Fit the initial baseline model
  fit.base<-gfit(g,mode=mode,diag=diag,tol=tol,rety=TRUE)
  fit<-list()  #Initialize output
  fit$coefficients<-qr.coef(fit.base[[1]],fit.base[[2]])
  fit$fitted.values<-qr.fitted(fit.base[[1]],fit.base[[2]])
  fit$residuals<-qr.resid(fit.base[[1]],fit.base[[2]])
  fit$qr<-fit.base[[1]]
  fit$rank<-fit.base[[1]]$rank
  fit$n<-length(fit.base[[2]])
  fit$df.residual<-fit$n-fit$rank
  tstat<-match.arg(test.statistic)
  if(tstat=="beta")
    fit$tstat<-fit$coefficients
  else if(tstat=="t-value")
    fit$tstat<-fit$coefficients/ sqrt(diag(chol2inv(fit$qr$qr))*sum(fit$residuals^2)/(fit$n-fit$rank))
  #Proceed based on selected null hypothesis
  nullhyp<-match.arg(nullhyp)
  if((nullhyp%in%c("qap","qapspp"))&&(nx==1))  #No partialling w/one predictor
    nullhyp<-"qapy"
  if(nullhyp=="classical"){
    resvar<-sum(fit$residuals^2)/fit$df.residual
    cvm<-chol2inv(fit$qr$qr)
    se<-sqrt(diag(cvm)*resvar)
    tval<-fit$coefficients/se
    #Prepare output
    fit$dist<-NULL
    fit$pleeq<-pt(tval,fit$df.residual)
    fit$pgreq<-pt(tval,fit$df.residual,lower.tail=FALSE)
    fit$pgreqabs<-2*pt(abs(tval),fit$df.residual,lower.tail=FALSE)
  }else if(nullhyp%in%c("cugtie","cugden","cuguman")){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    for(i in 1:nx){
      gr<-g
      for(j in 1:reps){
        #Modify the focal x
        gr[[i+1]]<-switch(nullhyp,
          cugtie=rgraph(n,mode=mode,diag=diag,replace=FALSE,tielist=g[[i+1]]),
          cugden=rgraph(n,tprob=gden(g[[i+1]],mode=mode,diag=diag),mode=mode, diag=diag),
          cuguman=(function(dc,n){rguman(1,n,mut=dc[1],asym=dc[2],null=dc[3], method="exact")})(dyad.census(g[[i+1]]),n)
        )
        #Fit model with modified x
        repdist[j,i]<-gfit(gr,mode=mode,diag=diag,tol=tol,rety=FALSE, tstat=tstat)[i]
      }
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if(nullhyp=="qapy"){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    gr<-g
    for(i in 1:reps){
      gr[[1]]<-rmperm(g[[1]])  #Permute y
      #Fit the model under replication
      repdist[i,]<-gfit(gr,mode=mode,diag=diag,tol=tol,rety=FALSE,tstat=tstat)
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if(nullhyp=="qapx"){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    for(i in 1:nx){
      gr<-g
      for(j in 1:reps){
        gr[[i+1]]<-rmperm(gr[[i+1]]) #Modify the focal x
        #Fit model with modified x
        repdist[j,i]<-gfit(gr,mode=mode,diag=diag,tol=tol,rety=FALSE, tstat=tstat)[i]
      }
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if(nullhyp=="qapallx"){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    gr<-g
    for(i in 1:reps){
      for(j in 1:nx)
        gr[[1+j]]<-rmperm(g[[1+j]])  #Permute each x
      #Fit the model under replication
      repdist[i,]<-gfit(gr,mode=mode,diag=diag,tol=tol,rety=FALSE, tstat=tstat)
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if((nullhyp=="qap")||(nullhyp=="qapspp")){
    xsel<-matrix(TRUE,n,n)
    if(!diag)
      diag(xsel)<-FALSE
    if(mode=="graph")
      xsel[upper.tri(xsel)]<-FALSE
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    for(i in 1:nx){
      #Regress x_i on other x's
      xfit<-gfit(g[1+c(i,(1:nx)[-i])],mode=mode,diag=diag,tol=tol,rety=TRUE, tstat=tstat)
      xres<-g[[1+i]]
      xres[xsel]<-qr.resid(xfit[[1]],xfit[[2]])  #Get residuals of x_i
      if(mode=="graph")
        xres[upper.tri(xres)]<-t(xres)[upper.tri(xres)]
      #Draw replicate coefs using permuted x residuals
      for(j in 1:reps)
        repdist[j,i]<-gfit(c(g[-(1+i)],list(rmperm(xres))),mode=mode,diag=diag, tol=tol,rety=FALSE,tstat=tstat)[nx]
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }
  #Finalize the results
  fit$nullhyp<-nullhyp
  fit$names<-paste("x",1:(nx-intercept),sep="")
  if(intercept)
    fit$names<-c("(intercept)",fit$names)
  fit$intercept<-intercept
  class(fit)<-"netlm"
  #Return the result
  fit
}


#netlogit - God help me, it's a network regression routine using a 
#binomial/logit GLM.  It's also frighteningly slow, since it's essentially a 
#front end to the builtin GLM routine with a bunch of network hypothesis testing
#stuff thrown in for good measure.
netlogit<-function(y,x,intercept=TRUE,mode="digraph",diag=FALSE,nullhyp=c("qap", "qapspp","qapy","qapx","qapallx","cugtie","cugden","cuguman","classical"), test.statistic=c("z-value","beta"), tol=1e-7,reps=1000){
  #Define an internal routine to quickly fit logit models to graphs
  gfit<-function(glist,mode,diag){
    y<-gvectorize(glist[[1]],mode=mode,diag=diag,censor.as.na=TRUE)
    x<-vector()
    for(i in 2:length(glist))
      x<-cbind(x,gvectorize(glist[[i]],mode=mode,diag=diag,censor.as.na=TRUE))
    if(!is.matrix(x))
      x<-matrix(x,ncol=1)
    mis<-is.na(y)|apply(is.na(x),1,any)
    glm.fit(x[!mis,],y[!mis],family=binomial(),intercept=FALSE)
  }
  #Repeat the above, for strictly linear models
  gfitlm<-function(glist,mode,diag,tol){
    y<-gvectorize(glist[[1]],mode=mode,diag=diag,censor.as.na=TRUE)
    x<-vector()
    for(i in 2:length(glist))
      x<-cbind(x,gvectorize(glist[[i]],mode=mode,diag=diag,censor.as.na=TRUE))
    if(!is.matrix(x))
      x<-matrix(x,ncol=1)
    mis<-is.na(y)|apply(is.na(x),1,any)
    list(qr(x[!mis,],tol=tol),y[!mis])
  }
  #Get the data in order
  y<-as.sociomatrix.sna(y)
  x<-as.sociomatrix.sna(x)
  if(is.list(y)||((length(dim(y))>2)&&(dim(y)[1]>1))) 
    stop("y must be a single graph in netlogit.")
  if(length(dim(y))>2)
    y<-y[1,,]
  if(is.list(x)||(dim(x)[2]!=dim(y)[2]))
    stop("Homogeneous graph orders required in netlogit.")
  nx<-stackcount(x)+intercept    #Get number of predictors
  n<-dim(y)[2]                   #Get graph order
  g<-list(y)                     #Put graphs into a list
  if(intercept)
    g[[2]]<-matrix(1,n,n)
  if(nx-intercept==1)
    g[[2+intercept]]<-x
  else
    for(i in 1:(nx-intercept))
      g[[i+1+intercept]]<-x[i,,]
  if(any(sapply(lapply(g,is.na),any)))
    warning("Missing data supplied to netlogit; this may pose problems for certain null hypotheses.  Hope you know what you're doing....")
  #Fit the initial baseline model
  fit.base<-gfit(g,mode=mode,diag=diag)
  fit<-list()  #Initialize output
  fit$coefficients<-fit.base$coefficients
  fit$fitted.values<-fit.base$fitted.values
  fit$residuals<-fit.base$residuals
  fit$se<-sqrt(diag(chol2inv(fit.base$qr$qr)))
  tstat<-match.arg(test.statistic)
  fit$test.statistic<-tstat
  if(tstat=="beta")
    fit$tstat<-fit$coefficients
  else if(tstat=="z-value")
    fit$tstat<-fit$coefficients/fit$se
  fit$linear.predictors<-fit.base$linear.predictors
  fit$n<-length(fit.base$y)
  fit$df.model<-fit.base$rank
  fit$df.residual<-fit.base$df.residual
  fit$deviance<-fit.base$deviance
  fit$null.deviance<-fit.base$null.deviance
  fit$df.null<-fit.base$df.null
  fit$aic<-fit.base$aic
  fit$bic<-fit$deviance+fit$df.model*log(fit$n)
  fit$qr<-fit.base$qr
  fit$ctable<-table(as.numeric(fit$fitted.values>=0.5),fit.base$y, dnn=c("Predicted","Actual"))  #Get the contingency table 
  if(NROW(fit$ctable)==1){
    if(rownames(fit$ctable)=="0")
      fit$ctable<-rbind(fit$ctable,c(0,0))
    else
      fit$ctable<-rbind(c(0,0),fit$ctable)
    rownames(fit$ctable)<-c("0","1")
  }

  #Proceed based on selected null hypothesis
  nullhyp<-match.arg(nullhyp)
  if((nullhyp%in%c("qap","qapspp"))&&(nx==1))  #No partialling w/one predictor
    nullhyp<-"qapy"
  if(nullhyp=="classical"){
    cvm<-chol2inv(fit$qr$qr)
    se<-sqrt(diag(cvm))
    tval<-fit$coefficients/se
    #Prepare output
    fit$dist<-NULL
    fit$pleeq<-pt(tval,fit$df.residual)
    fit$pgreq<-pt(tval,fit$df.residual,lower.tail=FALSE)
    fit$pgreqabs<-2*pt(abs(tval),fit$df.residual,lower.tail=FALSE)
  }else if(nullhyp%in%c("cugtie","cugden","cuguman")){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    for(i in 1:nx){
      gr<-g
      for(j in 1:reps){
        #Modify the focal x
        gr[[i+1]]<-switch(nullhyp,
          cugtie=rgraph(n,mode=mode,diag=diag,replace=FALSE,tielist=g[[i+1]]),
          cugden=rgraph(n,tprob=gden(g[[i+1]],mode=mode,diag=diag),mode=mode, diag=diag),
          cuguman=(function(dc,n){rguman(1,n,mut=dc[1],asym=dc[2],null=dc[3], method="exact")})(dyad.census(g[[i+1]]),n)
        )
        #Fit model with modified x
        repfit<-gfit(gr,mode=mode,diag=diag)
        if(tstat=="beta")
          repdist[j,i]<-repfit$coef[i]
        else
          repdist[j,i]<-repfit$coef[i]/sqrt(diag(chol2inv(repfit$qr$qr)))[i]
      }
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if(nullhyp=="qapy"){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    gr<-g
    for(i in 1:reps){
      gr[[1]]<-rmperm(g[[1]])  #Permute y
      #Fit the model under replication
      repfit<-gfit(gr,mode=mode,diag=diag)
      if(tstat=="beta")
        repdist[i,]<-repfit$coef
      else
        repdist[i,]<-repfit$coef/sqrt(diag(chol2inv(repfit$qr$qr)))
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if(nullhyp=="qapx"){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    for(i in 1:nx){
      gr<-g
      for(j in 1:reps){
        gr[[i+1]]<-rmperm(gr[[i+1]]) #Modify the focal x
        #Fit model with modified x
        repfit<-gfit(gr,mode=mode,diag=diag)
        if(tstat=="beta")
          repdist[j,i]<-repfit$coef[i]
        else
          repdist[j,i]<-repfit$coef[i]/sqrt(diag(chol2inv(repfit$qr$qr)))[i]
      }
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if(nullhyp=="qapallx"){
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    gr<-g
    for(i in 1:reps){
      for(j in 1:nx)
        gr[[1+j]]<-rmperm(g[[1+j]])  #Permute each x
      #Fit the model under replication
      repfit<-gfit(gr,mode=mode,diag=diag)
      if(tstat=="beta")
        repdist[i,]<-repfit$coef
      else
        repdist[i,]<-repfit$coef/sqrt(diag(chol2inv(repfit$qr$qr)))
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }else if((nullhyp=="qap")||(nullhyp=="qapspp")){
    xsel<-matrix(TRUE,n,n)
    if(!diag)
      diag(xsel)<-FALSE
    if(mode=="graph")
      xsel[upper.tri(xsel)]<-FALSE
    #Generate replicates for each predictor
    repdist<-matrix(0,reps,nx)
    for(i in 1:nx){
      #Regress x_i on other x's
      xfit<-gfitlm(g[1+c(i,(1:nx)[-i])],mode=mode,diag=diag,tol=tol)
      xres<-g[[1+i]]
      xres[xsel]<-qr.resid(xfit[[1]],xfit[[2]])  #Get residuals of x_i
      if(mode=="graph")
        xres[upper.tri(xres)]<-t(xres)[upper.tri(xres)]
      #Draw replicate coefs using permuted x residuals
      for(j in 1:reps){
        repfit<-gfit(c(g[-(1+i)],list(rmperm(xres))),mode=mode, diag=diag)
        if(tstat=="beta")
          repdist[j,i]<-repfit$coef[nx]
        else
          repdist[j,i]<-repfit$coef[nx]/sqrt(diag(chol2inv(repfit$qr$qr)))[nx]
      }
    }
    #Prepare output
    fit$dist<-repdist
    fit$pleeq<-apply(sweep(fit$dist,2,fit$tstat,"<="),2,mean)
    fit$pgreq<-apply(sweep(fit$dist,2,fit$tstat,">="),2,mean)
    fit$pgreqabs<-apply(sweep(abs(fit$dist),2,abs(fit$tstat),">="),2, mean)
  }
  #Finalize the results
  fit$nullhyp<-nullhyp
  fit$names<-paste("x",1:(nx-intercept),sep="")
  if(intercept)
    fit$names<-c("(intercept)",fit$names)
  fit$intercept<-intercept
  class(fit)<-"netlogit"
  #Return the result
  fit
}


#npostpred - Take posterior predictive draws for functions of networks.
npostpred<-function(b,FUN,...){
   #Find the desired function
   fun<-match.fun(FUN)
   #Take the draws
   out<-apply(b$net,1,fun,...)
   out
}


#plot.bbnam - Plot method for bbnam
plot.bbnam<-function(x,mode="density",intlines=TRUE,...){
   UseMethod("plot",x)
}


#plot.bbnam.actor - Plot method for bbnam.actor
plot.bbnam.actor<-function(x,mode="density",intlines=TRUE,...){
   #Get the initial graphical settings, so we can restore them later
   oldpar<-par(no.readonly=TRUE)
   #Change plotting params
   par(ask=dev.interactive())
   #Initial plot: global error distribution
   par(mfrow=c(2,1))
   if(mode=="density"){   #Approximate the pdf using kernel density estimation
      #Plot marginal population (i.e. across actors) density of p(false negative)
      plot(density(x$em),main=substitute(paste("Estimated Marginal Population Density of ",{e^{"-"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"-"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$em,c(0.05,0.5,0.95)),lty=c(3,2,3))
      #Plot marginal population (i.e. across actors) density of p(false positive)
      plot(density(x$ep),main=substitute(paste("Estimated Marginal Population Density of ",{e^{"+"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"+"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$ep,c(0.05,0.5,0.95)),lty=c(3,2,3))
   }else{     #Use histograms to plot the estimated density
      #Plot marginal population (i.e. across actors) density of p(false negative)
      hist(x$em,main=substitute(paste("Histogram of ",{e^{"-"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"-"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$em,c(0.05,0.5,0.95)),lty=c(3,2,3))
      #Plot marginal population (i.e. across actors) density of p(false positive)
      hist(x$ep,main=substitute(paste("Histogram of ",{e^{"+"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"+"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$ep,c(0.05,0.5,0.95)),lty=c(3,2,3))
   }
   #Plot e- next
   par(mfrow=c(floor(sqrt(x$nobservers)),ceiling(sqrt(x$nobservers))))
   for(i in 1:x$nobservers){
      if(mode=="density"){
         plot(density(x$em[,i]),main=substitute({e^{"-"}}[it],list(it=i)), xlab=substitute({e^{"-"}}[it],list(it=i)),xlim=c(0,1),...)
         #Plot interval lines if required.
         if(intlines)
            abline(v=quantile(x$em[,i],c(0.05,0.5,0.95)),lty=c(3,2,3))
      }else{
         hist(x$em[,i],main=substitute({e^{"-"}}[it],list(it=i)), xlab=substitute({e^{"-"}}[it],list(it=i)),xlim=c(0,1),...)
         #Plot interval lines if required.
         if(intlines)
            abline(v=quantile(x$em[,i],c(0.05,0.5,0.95)),lty=c(3,2,3))
      }
   }
   #Now plot e+
   par(mfrow=c(floor(sqrt(x$nobservers)),ceiling(sqrt(x$nobservers))))
   for(i in 1:x$nobservers){
      if(mode=="density"){
         plot(density(x$ep[,i]),main=substitute({e^{"+"}}[it],list(it=i)), xlab=substitute({e^{"+"}}[it],list(it=i)),xlim=c(0,1),...)
         #Plot interval lines if required.
         if(intlines)
            abline(v=quantile(x$ep[,i],c(0.05,0.5,0.95)),lty=c(3,2,3))
      }else{
         hist(x$ep[,i],main=substitute({e^{"+"}}[it],list(it=i)), xlab=substitute({e^{"+"}}[it],list(it=i)),xlim=c(0,1),...)
         #Plot interval lines if required.
         if(intlines)
            abline(v=quantile(x$ep[,i],c(0.05,0.5,0.95)),lty=c(3,2,3))
      }
   }
   #Finally, try to plot histograms of tie probabilities
   par(mfrow=c(1,1))
   plot.sociomatrix(apply(x$net,c(2,3),mean),labels=list(x$anames,x$anames),main="Marginal Posterior Tie Probability Distribution")
   #Clean up
   par(oldpar)
}


#plot.bbnam.fixed - Plot method for bbnam.fixed
plot.bbnam.fixed<-function(x,mode="density",intlines=TRUE,...){
   #Get the initial graphical settings, so we can restore them later
   oldpar<-par()
   #Perform matrix plot of tie probabilities
   par(mfrow=c(1,1))
   plot.sociomatrix(apply(x$net,c(2,3),mean),labels=list(x$anames,x$anames),main="Marginal Posterior Tie Probability Distribution")
   #Clean up
   par(oldpar)
}


#plot.bbnam.pooled - Plot method for bbnam.pooled
plot.bbnam.pooled<-function(x,mode="density",intlines=TRUE,...){
   #Get the initial graphical settings, so we can restore them later
   oldpar<-par()
   #Change plotting params
   par(ask=dev.interactive())
   #Initial plot: pooled error distribution
   par(mfrow=c(2,1))
   if(mode=="density"){   #Approximate the pdf using kernel density estimation
      #Plot marginal population (i.e. across actors) density of p(false negative)
      plot(density(x$em),main=substitute(paste("Estimated Marginal Posterior Density of ",{e^{"-"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"-"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$em,c(0.05,0.5,0.95)),lty=c(3,2,3))
      #Plot marginal population (i.e. across actors) density of p(false positive)
      plot(density(x$ep),main=substitute(paste("Estimated Marginal Posterior Density of ",{e^{"+"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"+"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$ep,c(0.05,0.5,0.95)),lty=c(3,2,3))
   }else{     #Use histograms to plot the estimated density
      #Plot marginal population (i.e. across actors) density of p(false negative)
      hist(x$em,main=substitute(paste("Histogram of ",{e^{"-"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"-"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$em,c(0.05,0.5,0.95)),lty=c(3,2,3))
      #Plot marginal population (i.e. across actors) density of p(false positive)
      hist(x$ep,main=substitute(paste("Histogram of ",{e^{"+"}},", ",draws," Draws"),list(draws=x$draws)),xlab=expression({e^{"+"}}),xlim=c(0,1),...)
      #Plot interval lines if required.
      if(intlines)
         abline(v=quantile(x$ep,c(0.05,0.5,0.95)),lty=c(3,2,3))
   }
   #Finally, try to plot histograms of tie probabilities
   par(mfrow=c(1,1))
   plot.sociomatrix(apply(x$net,c(2,3),mean),labels=list(x$anames,x$anames),main="Marginal Posterior Tie Probability Distribution")
   #Clean up
   par(oldpar)
}


#plot.bn - Plot method for bn
plot.bn<-function(x,...){
  op<-par(no.readonly=TRUE) #Store old plotting params
  on.exit(par(op))          #Reset when finished
  par(mfrow=c(2,2))
  #Dyad plot
  dc<-sum(x$dyads)          #Get # dyads
  dp<-x$dyads.pred          #Get dyad probs
  dpm<-x$dyads.pred*dc      #Get pred marginals
  dpsd<-sqrt(dp*(1-dp)*dc)  #Get pred SD
  dr<-range(c(x$dyads,dpm+1.96*dpsd,dpm-1.96*dpsd))  #Get range
  if(all(x$dyads>0)&&(all(dpm>0)))
    plot(1:3,dpm,axes=FALSE,ylim=dr,main="Predicted Dyad Census", xlab="Dyad Type",ylab="Count",log="y",col=2,xlim=c(0.5,3.5))
  else
    plot(1:3,dpm,axes=FALSE,ylim=dr,main="Predicted Dyad Census", xlab="Dyad Type",ylab="Count",col=2,xlim=c(0.5,3.5))
  segments(1:3,dpm-1.96*dpsd,1:3,dpm+1.96*dpsd,col=2)
  segments(1:3-0.3,dpm-1.96*dpsd,1:3+0.3,dpm-1.96*dpsd,col=2)
  segments(1:3-0.3,dpm+1.96*dpsd,1:3+0.3,dpm+1.96*dpsd,col=2)
  points(1:3,x$dyads,pch=19)
  axis(2)
  axis(1,at=1:3,labels=names(x$dyads),las=3)
  #Triad plot
  tc<-sum(x$triads)          #Get # triads
  tp<-x$triads.pred          #Get triad probs
  tpm<-x$triads.pred*tc      #Get pred marginals
  tpsd<-sqrt(tp*(1-tp)*tc)   #Get pred SD
  tr<-range(c(x$triads,tpm+1.96*tpsd,tpm-1.96*tpsd))  #Get range
  if(all(x$triads>0)&&(all(tpm>0)))
    plot(1:16,tpm,axes=FALSE,ylim=tr,main="Predicted Triad Census", xlab="Triad Type",ylab="Count",log="y",col=2)
  else
    plot(1:16,tpm,axes=FALSE,ylim=tr,main="Predicted Triad Census", xlab="Triad Type",ylab="Count",col=2)
  segments(1:16,tpm-1.96*tpsd,1:16,tpm+1.96*tpsd,col=2)
  segments(1:16-0.3,tpm-1.96*tpsd,1:16+0.3,tpm-1.96*tpsd,col=2)
  segments(1:16-0.3,tpm+1.96*tpsd,1:16+0.3,tpm+1.96*tpsd,col=2)
  points(1:16,x$triads,pch=19)
  axis(2)
  axis(1,at=1:16,labels=names(x$triads),las=3)
  #Structure statistics
  ssr<-range(c(x$ss,x$ss.pred))
  plot(0:(length(x$ss)-1),x$ss,type="b",xlab="Distance",ylab="Proportion Reached", main="Predicted Structure Statistics",ylim=ssr)
  lines(0:(length(x$ss)-1),x$ss.pred,col=2,lty=2)
}


#plot.lnam - Plot method for lnam
plot.lnam<-function(x,...){
   r<-residuals(x)
   f<-fitted(x)
   d<-x$disturbances
   sdr<-sd(r)
   ci<-c(-1.959964,1.959964)
   old.par <- par(no.readonly = TRUE)
   on.exit(par(old.par))
   par(mfrow=c(2,2))
   #Plot residual versus actual values
   plot(x$y,f,ylab=expression(hat(y)),xlab=expression(y),main="Fitted vs. Observed Values")
   abline(ci[1]*sdr,1,lty=3)
   abline(0,1,lty=2)
   abline(ci[2]*sdr,1,lty=3)
   #Plot disturbances versus fitted values
   plot(f,d,ylab=expression(hat(nu)),xlab=expression(hat(y)), ylim=c(min(ci[1]*x$sigma,d),max(ci[2]*x$sigma,d)),main="Fitted Values vs. Estimated Disturbances")
   abline(h=c(ci[1]*x$sigma,0,ci[2]*x$sigma),lty=c(3,2,3))
   #QQ-Plot the residuals
   qqnorm(r,main="Normal Q-Q Residual Plot")
   qqline(r)
   #Plot an influence diagram
   if(!(is.null(x$W1)&&is.null(x$W2))){
      inf<-matrix(0,ncol=x$df.total,nrow=x$df.total)
      if(!is.null(x$W1))
         inf<-inf+qr.solve(diag(x$df.total)-apply(sweep(x$W1,1,x$rho1,"*"), c(2,3),sum))
      if(!is.null(x$W2))
         inf<-inf+qr.solve(diag(x$df.total)-apply(sweep(x$W2,1,x$rho2,"*"), c(2,3),sum))
      syminf<-abs(inf)+abs(t(inf))
      diag(syminf)<-0
      infco<-cmdscale(as.dist(max(syminf)-syminf),k=2)
      diag(inf)<-NA
      stdinf<-inf-mean(inf,na.rm=TRUE)
      infsd<-sd(as.vector(stdinf),na.rm=TRUE)
      stdinf<-stdinf/infsd
      gplot(abs(stdinf),thresh=1.96,coord=infco,main="Net Influence Plot",edge.lty=1,edge.lwd=abs(stdinf)/2,edge.col=2+(inf>0)) 
   }
   #Restore plot settings
   invisible()
}


#potscalered.mcmc - Potential scale reduction (sqrt(Rhat)) for scalar estimands.
#Input must be a matrix whose columns correspond to replicate chains.  This, 
#clearly, doesn't belong here, but lacking a better place to put it I have 
#included it nonetheless.
potscalered.mcmc<-function(psi){
   #Use Gelman et al. notation, for convenience
   J<-dim(psi)[2]
   n<-dim(psi)[1]
   #Find between-group variance estimate
   mpsij<-apply(psi,2,mean)
   mpsitot<-mean(mpsij)
   B<-(n/(J-1))*sum((mpsij-mpsitot)^2)
   #Find within-group variance estimate
   s2j<-apply(psi,2,var)
   W<-mean(s2j)
   #Calculate the (estimated) marginal posterior variance of the estimand
   varppsi<-((n-1)/n)*W+(1/n)*B
   #Return the potential scale reduction estimate
   sqrt(varppsi/W)
}


#print.bayes.factor - A fairly generic routine for printing bayes factors, here used for the bbnam routine.
print.bayes.factor<-function(x,...){
   tab<-x$int.lik
   rownames(tab)<-x$model.names
   colnames(tab)<-x$model.names
   cat("Log Bayes Factors by Model:\n\n(Diagonals indicate raw integrated log likelihood estimates.)\n\n")
   print(tab)
   cat("\n")   
}


#print.bbnam - Print method for bbnam
print.bbnam<-function(x,...){
   UseMethod("print",x)
}


#print.bbnam.actor - Print method for bbnam.actor
print.bbnam.actor<-function(x,...){
   cat("\nButts' Hierarchical Bayes Model for Network Estimation/Informant Accuracy\n\n")
   cat("Multiple Error Probability Model\n\n")
   #Dump marginal posterior network
   cat("Marginal Posterior Network Distribution:\n\n")
   d<-apply(x$net,c(2,3),mean)
   rownames(d)<-as.vector(x$anames)
   colnames(d)<-as.vector(x$anames)
   print.table(d,digits=2)
   cat("\n")
   #Dump summary of error probabilities
   cat("Marginal Posterior Global Error Distribution:\n\n")
   d<-matrix(ncol=2,nrow=6)
   d[1:3,1]<-quantile(x$em,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,1]<-mean(x$em,na.rm=TRUE)
   d[5:6,1]<-quantile(x$em,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   d[1:3,2]<-quantile(x$ep,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,2]<-mean(x$ep,na.rm=TRUE)
   d[5:6,2]<-quantile(x$ep,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   colnames(d)<-c("e^-","e^+")
   rownames(d)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
   print.table(d,digits=4)
   cat("\n")
}


#print.bbnam.fixed - Print method for bbnam.fixed
print.bbnam.fixed<-function(x,...){
   cat("\nButts' Hierarchical Bayes Model for Network Estimation/Informant Accuracy\n\n")
   cat("Fixed Error Probability Model\n\n")
   #Dump marginal posterior network
   cat("Marginal Posterior Network Distribution:\n\n")
   d<-apply(x$net,c(2,3),mean)
   rownames(d)<-as.vector(x$anames)
   colnames(d)<-as.vector(x$anames)
   print.table(d,digits=2)
   cat("\n")
}


#print.bbnam.pooled - Print method for bbnam.pooled
print.bbnam.pooled<-function(x,...){
   cat("\nButts' Hierarchical Bayes Model for Network Estimation/Informant Accuracy\n\n")
   cat("Pooled Error Probability Model\n\n")
   #Dump marginal posterior network
   cat("Marginal Posterior Network Distribution:\n\n")
   d<-apply(x$net,c(2,3),mean)
   rownames(d)<-as.vector(x$anames)
   colnames(d)<-as.vector(x$anames)
   print.table(d,digits=2)
   cat("\n")
   #Dump summary of error probabilities
   cat("Marginal Posterior Global Error Distribution:\n\n")
   d<-matrix(ncol=2,nrow=6)
   d[1:3,1]<-quantile(x$em,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,1]<-mean(x$em,na.rm=TRUE)
   d[5:6,1]<-quantile(x$em,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   d[1:3,2]<-quantile(x$ep,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,2]<-mean(x$ep,na.rm=TRUE)
   d[5:6,2]<-quantile(x$ep,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   colnames(d)<-c("e^-","e^+")
   rownames(d)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
   print.table(d,digits=4)
   cat("\n")
}


#print.bn - Print method for summary.bn
print.bn<-function(x, digits=max(4,getOption("digits")-3), ...){
  cat("\nBiased Net Model\n\n")
  cat("Parameters:\n\n")
  cmat<-matrix(c(x$d,x$pi,x$sigma,x$rho),ncol=1)
  colnames(cmat)<-"Estimate"
  rownames(cmat)<-c("d","pi","sigma","rho")
  printCoefmat(cmat,digits=digits,...)
  cat("\n")
}


#print.lnam - Print method for lnam
print.lnam<-function(x, digits = max(3, getOption("digits") - 3), ...){
   cat("\nCall:\n", deparse(x$call), "\n\n", sep = "")
   cat("Coefficients:\n")
   print.default(format(coef(x), digits = digits), print.gap = 2, quote = FALSE)
   cat("\n")
}


#print.netcancor - Print method for netcancor
print.netcancor<-function(x,...){
   cat("\nCanonical Network Correlation\n\n")

   cat("Canonical Correlations:\n\n")
   cmat<-matrix(data=x$cor,ncol=length(x$cor),nrow=1)
   rownames(cmat)<-""
   colnames(cmat)<-as.vector(x$cnames)
   print.table(cmat)
   cat("\n")
   cat("Pr(>=cor):\n\n")
   cmat <- matrix(data=format(x$cpgreq),ncol=length(x$cpgreq),nrow=1)
   colnames(cmat) <- as.vector(x$cnames)
   rownames(cmat)<- ""
   print.table(cmat)
   cat("\n")
   cat("Pr(<=cor):\n\n")
   cmat <- matrix(data=format(x$cpleeq),ncol=length(x$cpleeq),nrow=1)
   colnames(cmat) <- as.vector(x$cnames)
   rownames(cmat)<- ""
   print.table(cmat)
   cat("\n")

   cat("X Coefficients:\n\n")
   cmat <- format(x$xcoef)
   colnames(cmat) <- as.vector(x$xnames)
   rownames(cmat)<- as.vector(x$xnames)
   print.table(cmat)
   cat("\n")
   cat("Pr(>=xcoef):\n\n")
   cmat <- format(x$xpgreq)
   colnames(cmat) <- as.vector(x$xnames)
   rownames(cmat)<- as.vector(x$xnames)
   print.table(cmat)
   cat("\n")
   cat("Pr(<=xcoef):\n\n")
   cmat <- format(x$xpleeq)
   colnames(cmat) <- as.vector(x$xnames)
   rownames(cmat)<- as.vector(x$xnames)
   print.table(cmat)
   cat("\n")

   cat("Y Coefficients:\n\n")
   cmat <- format(x$ycoef)
   colnames(cmat) <- as.vector(x$ynames)
   rownames(cmat)<- as.vector(x$ynames)
   print.table(cmat)
   cat("\n")
   cat("Pr(>=ycoef):\n\n")
   cmat <- format(x$ypgreq)
   colnames(cmat) <- as.vector(x$ynames)
   rownames(cmat)<- as.vector(x$ynames)
   print.table(cmat)
   cat("\n")
   cat("Pr(<=ycoef):\n\n")
   cmat <- format(x$ypleeq)
   colnames(cmat) <- as.vector(x$ynames)
   rownames(cmat)<- as.vector(x$ynames)
   print.table(cmat)
   cat("\n")
}


#print.netlm - Print method for netlm
print.netlm<-function(x,...){
   cat("\nOLS Network Model\n\n")
   cat("Coefficients:\n")
   cmat <- as.vector(format(as.numeric(x$coefficients)))
   cmat <- cbind(cmat, as.vector(format(x$pleeq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreqabs)))
   colnames(cmat) <- c("Estimate", "Pr(<=b)", "Pr(>=b)","Pr(>=|b|)")
   rownames(cmat)<- as.vector(x$names)
   print.table(cmat)
   #Goodness of fit measures
   mss<-if(x$intercept)
      sum((fitted(x)-mean(fitted(x)))^2)
   else
      sum(fitted(x)^2)
   rss<-sum(resid(x)^2)
   qn<-NROW(x$qr$qr)
   df.int<-x$intercept
   rdf<-qn-x$rank
   resvar<-rss/rdf
   fstatistic<-c(value=(mss/(x$rank-df.int))/resvar,numdf=x$rank-df.int, dendf=rdf)
   r.squared<-mss/(mss+rss)
   adj.r.squared<-1-(1-r.squared)*((qn-df.int)/rdf)
   sigma<-sqrt(resvar)
   cat("\nResidual standard error:",format(sigma,digits=4),"on",rdf,"degrees of freedom\n")
   cat("F-statistic:",formatC(fstatistic[1],digits=4),"on",fstatistic[2],"and", fstatistic[3],"degrees of freedom, p-value:",formatC(1-pf(fstatistic[1],fstatistic[2],fstatistic[3]),digits=4),"\n")
   cat("Multiple R-squared:",format(r.squared,digits=4),"\t")
   cat("Adjusted R-squared:",format(adj.r.squared,digits=4),"\n")
   cat("\n")
}


#print.netlogit - Print method for netlogit
print.netlogit<-function(x,...){
   cat("\nNetwork Logit Model\n\n")
   cat("Coefficients:\n")
   cmat <- as.vector(format(as.numeric(x$coefficients)))
   cmat <- cbind(cmat, as.vector(format(exp(as.numeric(x$coefficients)))))
   cmat <- cbind(cmat, as.vector(format(x$pleeq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreqabs)))
   colnames(cmat) <- c("Estimate", "Exp(b)", "Pr(<=b)", "Pr(>=b)", "Pr(>=|b|)")
   rownames(cmat)<- as.vector(x$names)
   print.table(cmat)
   cat("\nGoodness of Fit Statistics:\n")
   cat("\nNull deviance:",x$null.deviance,"on",x$df.null,"degrees of freedom\n")
   cat("Residual deviance:",x$deviance,"on",x$df.residual,"degrees of freedom\n")
   cat("Chi-Squared test of fit improvement:\n\t",x$null.deviance-x$deviance,"on",x$df.null-x$df.residual,"degrees of freedom, p-value",1-pchisq(x$null.deviance-x$deviance,df=x$df.null-x$df.residual),"\n") 
   cat("AIC:",x$aic,"\tBIC:",x$bic,"\nPseudo-R^2 Measures:\n\t(Dn-Dr)/(Dn-Dr+dfn):",(x$null.deviance-x$deviance)/(x$null.deviance-x$deviance+x$df.null),"\n\t(Dn-Dr)/Dn:",1-x$deviance/x$null.deviance,"\n")
   cat("\n")
}


#print.summary.bayes.factor - Printing for bayes factor summary objects
print.summary.bayes.factor<-function(x,...){
   cat("Log Bayes Factors by Model:\n\n(Diagonals indicate raw integrated log likelihood estimates.)\n\n")
   print(x$int.lik)
   stdtab<-matrix(x$int.lik.std,nrow=1)
   colnames(stdtab)<-x$model.names
   cat("\n\nLog Inverse Bayes Factors:\n\n(Diagonals indicate log posterior probability of model under within-set choice constraints and uniform model priors.\n\n")
   print(x$inv.bf)
   cat("\nEstimated model probabilities (within-set):\n")
   temp<-exp(diag(x$inv.bf))
   names(temp)<-x$model.names
   print(temp)
   cat("\n\nDiagnostics:\n\nReplications - ",x$reps,"\n\nLog std deviations of integrated likelihood estimates:\n")
   names(x$int.lik.std)<-x$model.names
   print(x$int.lik.std)
   cat("\n\nVector of hyperprior parameters:\n\n")
   priortab<-matrix(x$prior.param,nrow=1,ncol=length(x$prior.param))
   colnames(priortab)<-x$prior.param.names
   print(priortab)
   cat("\n\n")   
}


#print.summary.bbnam - Print method for summary.bbnam
print.summary.bbnam<-function(x,...){
   UseMethod("print",x)
}


#print.summary.bbnam.actor - Print method for summary.bbnam.actor
print.summary.bbnam.actor<-function(x,...){
   cat("\nButts' Hierarchical Bayes Model for Network Estimation/Informant Accuracy\n\n")
   cat("Multiple Error Probability Model\n\n")
   #Dump marginal posterior network
   cat("Marginal Posterior Network Distribution:\n\n")
   d<-apply(x$net,c(2,3),mean)
   rownames(d)<-as.vector(x$anames)
   colnames(d)<-as.vector(x$anames)
   print.table(d,digits=2)
   cat("\n")
   #Dump summary of error probabilities
   cat("Marginal Posterior Global Error Distribution:\n\n")
   d<-matrix(ncol=2,nrow=6)
   d[1:3,1]<-quantile(x$em,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,1]<-mean(x$em,na.rm=TRUE)
   d[5:6,1]<-quantile(x$em,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   d[1:3,2]<-quantile(x$ep,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,2]<-mean(x$ep,na.rm=TRUE)
   d[5:6,2]<-quantile(x$ep,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   colnames(d)<-c("e^-","e^+")
   rownames(d)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
   print.table(d,digits=4)
   cat("\n")
   #Dump error probability estimates per observer
   cat("Marginal Posterior Error Distribution (by observer):\n\n")
   cat("Probability of False Negatives (e^-):\n\n")
   d<-matrix(ncol=6)
   for(i in 1:x$nobservers){
      dv<-matrix(c(quantile(x$em[,i],c(0,0.25,0.5),names=FALSE,na.rm=TRUE),mean(x$em[,i],na.rm=TRUE),quantile(x$em[,i],c(0.75,1.0),names=FALSE,na.rm=TRUE)),nrow=1,ncol=6)
      d<-rbind(d,dv)
   }
   d<-d[2:(x$nobservers+1),]
   rownames(d)<-as.vector(x$onames)
   colnames(d)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
   print.table(d,digits=4)
   cat("\n")
   cat("Probability of False Positives (e^+):\n\n")
   d<-matrix(ncol=6)
   for(i in 1:x$nobservers){
      dv<-matrix(c(quantile(x$ep[,i],c(0,0.25,0.5),names=FALSE,na.rm=TRUE),mean(x$ep[,i],na.rm=TRUE),quantile(x$ep[,i],c(0.75,1.0),names=FALSE,na.rm=TRUE)),nrow=1,ncol=6)
      d<-rbind(d,dv)
   }
   d<-d[2:(x$nobservers+1),]
   rownames(d)<-as.vector(x$onames)
   colnames(d)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
   print.table(d,digits=4)
   cat("\n")
   #Dump MCMC diagnostics
   cat("MCMC Diagnostics:\n\n")
   cat("\tReplicate Chains:",x$reps,"\n")
   cat("\tBurn Time:",x$burntime,"\n")
   cat("\tDraws per Chain:",x$draws/x$reps,"Total Draws:",x$draws,"\n")
   if("sqrtrhat" %in% names(x))
      cat("\tPotential Scale Reduction (G&R's sqrt(Rhat)):\n \t\tMax:",max(x$sqrtrhat[!is.nan(x$sqrtrhat)]),"\n\t\tMed:",median(x$sqrtrhat[!is.nan(x$sqrtrhat)]),"\n\t\tIQR:",IQR(x$sqrtrhat[!is.nan(x$sqrtrhat)]),"\n")
   cat("\n")
}


#print.summary.bbnam.fixed - Print method for summary.bbnam.fixed
print.summary.bbnam.fixed<-function(x,...){
   cat("\nButts' Hierarchical Bayes Model for Network Estimation/Informant Accuracy\n\n")
   cat("Fixed Error Probability Model\n\n")
   #Dump marginal posterior network
   cat("Marginal Posterior Network Distribution:\n\n")
   d<-apply(x$net,c(2,3),mean)
   rownames(d)<-as.vector(x$anames)
   colnames(d)<-as.vector(x$anames)
   print.table(d,digits=2)
   cat("\n")
   #Dump model diagnostics
   cat("Model Diagnostics:\n\n")
   cat("\tTotal Draws:",x$draws,"\n\t(Note: Draws taken directly from network posterior.)")
   cat("\n")
}


#print.summary.bbnam.pooled - Print method for summary.bbnam.pooled
print.summary.bbnam.pooled<-function(x,...){
   cat("\nButts' Hierarchical Bayes Model for Network Estimation/Informant Accuracy\n\n")
   cat("Pooled Error Probability Model\n\n")
   #Dump marginal posterior network
   cat("Marginal Posterior Network Distribution:\n\n")
   d<-apply(x$net,c(2,3),mean)
   rownames(d)<-as.vector(x$anames)
   colnames(d)<-as.vector(x$anames)
   print.table(d,digits=2)
   cat("\n")
   #Dump summary of error probabilities
   cat("Marginal Posterior Error Distribution:\n\n")
   d<-matrix(ncol=2,nrow=6)
   d[1:3,1]<-quantile(x$em,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,1]<-mean(x$em,na.rm=TRUE)
   d[5:6,1]<-quantile(x$em,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   d[1:3,2]<-quantile(x$ep,c(0,0.25,0.5),names=FALSE,na.rm=TRUE)
   d[4,2]<-mean(x$ep,na.rm=TRUE)
   d[5:6,2]<-quantile(x$ep,c(0.75,1.0),names=FALSE,na.rm=TRUE)
   colnames(d)<-c("e^-","e^+")
   rownames(d)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
   print.table(d,digits=4)
   cat("\n")
   #Dump MCMC diagnostics
   cat("MCMC Diagnostics:\n\n")
   cat("\tReplicate Chains:",x$reps,"\n")
   cat("\tBurn Time:",x$burntime,"\n")
   cat("\tDraws per Chain:",x$draws/x$reps,"Total Draws:",x$draws,"\n")
   if("sqrtrhat" %in% names(x))
      cat("\tPotential Scale Reduction (G&R's sqrt(Rhat)):\n \t\tMax:",max(x$sqrtrhat[!is.nan(x$sqrtrhat)]),"\n\t\tMed:",median(x$sqrtrhat[!is.nan(x$sqrtrhat)]),"\n\t\tIQR:",IQR(x$sqrtrhat[!is.nan(x$sqrtrhat)]),"\n")
   cat("\n")
}


#print.summary.bn - Print method for summary.bn
print.summary.bn<-function(x, digits=max(4,getOption("digits")-3), signif.stars=getOption("show.signif.stars"), ...){
  cat("\nBiased Net Model\n\n")
  cat("\nParameters:\n\n")
  cmat<-matrix(c(x$d,x$pi,x$sigma,x$rho),ncol=1)
  colnames(cmat)<-"Estimate"
  rownames(cmat)<-c("d","pi","sigma","rho")
  printCoefmat(cmat,digits=digits,...)
  #Diagnostics
  cat("\nDiagnostics:\n\n")
  cat("\tFit method:",x$method,"\n")
  cat("\tPseudolikelihood G^2:",x$G.square,"\n")
  #Plot edge census
  cat("\n\tEdge census comparison:\n\n")
  ec<-sum(x$edges)
  cmat<-cbind(x$edges,x$edges.pred*ec)
  cmat<-cbind(cmat,(cmat[,1]-cmat[,2])/sqrt(x$edges.pred*(1-x$edges.pred)*ec))
  cmat<-cbind(cmat,2*(1-pnorm(abs(cmat[,3]))))
  colnames(cmat)<-c("Observed","Predicted","Z Value","Pr(>|z|)")
  printCoefmat(cmat,digits=digits,signif.stars=signif.stars,...)
  chisq<-sum((cmat[,1]-cmat[,2])^2/cmat[,2])
  cat("\tChi-Square:",chisq,"on 1 degrees of freedom.  p-value:",1-pchisq(chisq,1),"\n\n")
  #Plot dyad census
  cat("\n\tDyad census comparison:\n\n")
  dc<-sum(x$dyads)
  cmat<-cbind(x$dyads,x$dyads.pred*dc)
  cmat<-cbind(cmat,(cmat[,1]-cmat[,2])/sqrt(x$dyads.pred*(1-x$dyads.pred)*dc))
  cmat<-cbind(cmat,2*(1-pnorm(abs(cmat[,3]))))
  colnames(cmat)<-c("Observed","Predicted","Z Value","Pr(>|z|)")
  printCoefmat(cmat,digits=digits,signif.stars=signif.stars,...)
  chisq<-sum((cmat[,1]-cmat[,2])^2/cmat[,2])
  cat("\tChi-Square:",chisq,"on 2 degrees of freedom.  p-value:",1-pchisq(chisq,2),"\n\n")
  #Plot triad census
  cat("\n\tTriad census comparison:\n\n")
  tc<-sum(x$triads)
  cmat<-cbind(x$triads,x$triads.pred*tc)
  cmat<-cbind(cmat,(cmat[,1]-cmat[,2])/sqrt(x$triads.pred*(1-x$triads.pred)*tc))
  cmat<-cbind(cmat,2*(1-pnorm(abs(cmat[,3]))))
  colnames(cmat)<-c("Observed","Predicted","Z Value","Pr(>|z|)")
  printCoefmat(cmat,digits=digits,signif.stars=signif.stars,...)
  chisq<-sum((cmat[,1]-cmat[,2])^2/cmat[,2])
  cat("\tChi-Square:",chisq,"on 15 degrees of freedom.  p-value:",1-pchisq(chisq,15),"\n\n")
}


#print.summary.brokerage - print method for summary.brokerage objects
print.summary.brokerage<-function(x,...){
  cat("Gould-Fernandez Brokerage Analysis\n\n")
  cat("Global Brokerage Properties\n")
  cmat<-cbind(x$raw.gli,x$exp.gli,x$sd.gli,x$z.gli,2*(1-pnorm(abs(x$z.gli))))
  rownames(cmat)<-names(x$raw.gli)
  colnames(cmat)<-c("t","E(t)","Sd(t)","z","Pr(>|z|)")
  printCoefmat(cmat)
  cat("\nIndividual Properties (by Group)\n")
  for(i in x$clid){
    cat("\n\tGroup ID:",i,"\n")
    temp<-x$cl==i
    cmat<-cbind(x$raw.nli,x$z.nli)[temp,,drop=FALSE]
    print(cmat)
  }
  cat("\n")
}


#print.summary.lnam - Print method for summary.lnam
print.summary.lnam<-function(x, digits = max(3, getOption("digits") - 3), signif.stars = getOption("show.signif.stars"), ...){
   cat("\nCall:\n")
   cat(paste(deparse(x$call), sep = "\n", collapse = "\n"), "\n\n", sep = "")
   cat("Residuals:\n")
   nam <- c("Min", "1Q", "Median", "3Q", "Max")
   resid<-x$residuals 
   rq <- if (length(dim(resid)) == 2) 
      structure(apply(t(resid), 1, quantile), dimnames = list(nam, dimnames(resid)[[2]]))
   else structure(quantile(resid), names = nam)
   print(rq, digits = digits, ...)
   cat("\nCoefficients:\n")
   cmat<-cbind(coef(x),se.lnam(x))
   cmat<-cbind(cmat,cmat[,1]/cmat[,2],(1-pnorm(abs(cmat[,1]),0,cmat[,2]))*2)
   colnames(cmat)<-c("Estimate","Std. Error","Z value","Pr(>|z|)")
   #print(format(cmat,digits=digits),quote=FALSE)
   printCoefmat(cmat,digits=digits,signif.stars=signif.stars,...)
   cat("\n")
   cmat<-cbind(x$sigma,x$sigma.se)
   colnames(cmat)<-c("Estimate","Std. Error")
   rownames(cmat)<-"Sigma"
   printCoefmat(cmat,digits=digits,signif.stars=signif.stars,...)
   cat("\nGoodness-of-Fit:\n")
   rss<-sum(x$residuals^2)
   mss<-sum((x$fitted-mean(x$fitted))^2)
   rdfns<-x$df.residual+1
   cat("\tResidual standard error: ",format(sqrt(rss/rdfns),digits=digits)," on ",rdfns," degrees of freedom (w/o Sigma)\n",sep="")
   cat("\tMultiple R-Squared: ",format(mss/(mss+rss),digits=digits),", Adjusted R-Squared: ",format(1-(1-mss/(mss+rss))*x$df.total/rdfns,digits=digits),"\n",sep="")
   cat("\tModel log likelihood:", format(x$lnlik.model,digits=digits), "on", x$df.resid, "degrees of freedom (w/Sigma)\n\tAIC:",format(-2*x$lnlik.model+2*x$df.model,digits=digits),"BIC:",format(-2*x$lnlik.model+log(x$df.total)*x$df.model,digits=digits),"\n")
   cat("\n\tNull model:",x$null.model,"\n")
   cat("\tNull log likelihood:", format(x$lnlik.null,digits=digits), "on", x$df.null.resid, "degrees of freedom\n\tAIC:",format(-2*x$lnlik.null+2*x$df.null,digits=digits),"BIC:",format(-2*x$lnlik.null+log(x$df.total)*x$df.null,digits=digits),"\n")
   cat("\tAIC difference (model versus null):",format(-2*x$lnlik.null+2*x$df.null+2*x$lnlik.model-2*x$df.model,digits=digits),"\n")
   cat("\tHeuristic Log Bayes Factor (model versus null): ",format(-2*x$lnlik.null+log(x$df.total)*x$df.null+2*x$lnlik.model-log(x$df.total)*x$df.model,digits=digits),"\n")
   cat("\n")
}


#print.summary.netcancor - Print method for summary.netcancor
print.summary.netcancor<-function(x,...){
   cat("\nCanonical Network Correlation\n\n")

   cat("Canonical Correlations:\n\n")
   cmat<-as.vector(x$cor)
   cmat<-rbind(cmat,as.vector((x$cor)^2))
   rownames(cmat)<-c("Correlation","Coef. of Det.")
   colnames(cmat)<-as.vector(x$cnames)
   print.table(cmat)
   cat("\n")
   cat("Pr(>=cor):\n\n")
   cmat <- matrix(data=format(x$cpgreq),ncol=length(x$cpgreq),nrow=1)
   colnames(cmat) <- as.vector(x$cnames)
   rownames(cmat)<- ""
   print.table(cmat)
   cat("\n")
   cat("Pr(<=cor):\n\n")
   cmat <- matrix(data=format(x$cpleeq),ncol=length(x$cpleeq),nrow=1)
   colnames(cmat) <- as.vector(x$cnames)
   rownames(cmat)<- ""
   print.table(cmat)
   cat("\n")

   cat("X Coefficients:\n\n")
   cmat <- format(x$xcoef)
   colnames(cmat) <- as.vector(x$xnames)
   rownames(cmat)<- as.vector(x$xnames)
   print.table(cmat)
   cat("\n")
   cat("Pr(>=xcoef):\n\n")
   cmat <- format(x$xpgreq)
   colnames(cmat) <- as.vector(x$xnames)
   rownames(cmat)<- as.vector(x$xnames)
   print.table(cmat)
   cat("\n")
   cat("Pr(<=xcoef):\n\n")
   cmat <- format(x$xpleeq)
   colnames(cmat) <- as.vector(x$xnames)
   rownames(cmat)<- as.vector(x$xnames)
   print.table(cmat)
   cat("\n")

   cat("Y Coefficients:\n\n")
   cmat <- format(x$ycoef)
   colnames(cmat) <- as.vector(x$ynames)
   rownames(cmat)<- as.vector(x$ynames)
   print.table(cmat)
   cat("\n")
   cat("Pr(>=ycoef):\n\n")
   cmat <- format(x$ypgreq)
   colnames(cmat) <- as.vector(x$ynames)
   rownames(cmat)<- as.vector(x$ynames)
   print.table(cmat)
   cat("\n")
   cat("Pr(<=ycoef):\n\n")
   cmat <- format(x$ypleeq)
   colnames(cmat) <- as.vector(x$ynames)
   rownames(cmat)<- as.vector(x$ynames)
   print.table(cmat)
   cat("\n")

   cat("Test Diagnostics:\n\n")
   cat("\tNull Hypothesis:")
   if(x$nullhyp=="qap")
      cat(" QAP\n")
   else
      cat(" CUG\n")
   cat("\tReplications:",dim(x$cdist)[1],"\n")
   cat("\tDistribution Summary for Correlations:\n\n")
   dmat<-apply(x$cdist,2,min,na.rm=TRUE)
   dmat<-rbind(dmat,apply(x$cdist,2,quantile,probs=0.25,names=FALSE,na.rm=TRUE))
   dmat<-rbind(dmat,apply(x$cdist,2,quantile,probs=0.5,names=FALSE,na.rm=TRUE))
   dmat<-rbind(dmat,apply(x$cdist,2,mean,na.rm=TRUE))
   dmat<-rbind(dmat,apply(x$cdist,2,quantile,probs=0.75,names=FALSE,na.rm=TRUE))
   dmat<-rbind(dmat,apply(x$cdist,2,max,na.rm=TRUE))
   colnames(dmat)<-as.vector(x$cnames)
   rownames(dmat)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
   print.table(dmat,digits=4)
   cat("\n")
}


#print.summary.netlm - Print method for summary.netlm
print.summary.netlm<-function(x,...){
   cat("\nOLS Network Model\n\n")
   #Residuals
   cat("Residuals:\n")
   print.table(format(quantile(x$residuals)))
   #Coefficients
   cat("\nCoefficients:\n")
   cmat <- as.vector(format(as.numeric(x$coefficients)))
   cmat <- cbind(cmat, as.vector(format(x$pleeq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreqabs)))
   colnames(cmat) <- c("Estimate", "Pr(<=b)", "Pr(>=b)", "Pr(>=|b|)")
   rownames(cmat)<- as.vector(x$names)
   print.table(cmat)
   #Goodness of fit measures
   mss<-if(x$intercept)
      sum((fitted(x)-mean(fitted(x)))^2)
   else
      sum(fitted(x)^2)
   rss<-sum(resid(x)^2)
   qn<-NROW(x$qr$qr)
   df.int<-x$intercept
   rdf<-qn-x$rank
   resvar<-rss/rdf
   fstatistic<-c(value=(mss/(x$rank-df.int))/resvar,numdf=x$rank-df.int, dendf=rdf)
   r.squared<-mss/(mss+rss)
   adj.r.squared<-1-(1-r.squared)*((qn-df.int)/rdf)
   sigma<-sqrt(resvar)
   cat("\nResidual standard error:",format(sigma,digits=4),"on",rdf,"degrees of freedom\n")
   cat("Multiple R-squared:",format(r.squared,digits=4),"\t")
   cat("Adjusted R-squared:",format(adj.r.squared,digits=4),"\n")
   cat("F-statistic:",formatC(fstatistic[1],digits=4),"on",fstatistic[2],"and", fstatistic[3],"degrees of freedom, p-value:",formatC(1-pf(fstatistic[1],fstatistic[2],fstatistic[3]),digits=4),"\n")
   #Test diagnostics
   cat("\n\nTest Diagnostics:\n\n")
   cat("\tNull Hypothesis:",x$nullhyp,"\n")
   if(!is.null(x$dist)){
     cat("\tReplications:",dim(x$dist)[1],"\n")
     cat("\tCoefficient Distribution Summary:\n\n")
     dmat<-apply(x$dist,2,min,na.rm=TRUE)
     dmat<-rbind(dmat,apply(x$dist,2,quantile,probs=0.25,names=FALSE, na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,quantile,probs=0.5,names=FALSE,na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,mean,na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,quantile,probs=0.75,names=FALSE, na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,max,na.rm=TRUE))
     colnames(dmat)<-as.vector(x$names)
     rownames(dmat)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
     print.table(dmat,digits=4)
     cat("\n")
  }
}


#print.summary.netlogit - Print method for summary.netlogit
print.summary.netlogit<-function(x,...){
   cat("\nNetwork Logit Model\n\n")
   cat("Coefficients:\n")
   cmat <- as.vector(format(as.numeric(x$coefficients)))
   cmat <- cbind(cmat, as.vector(format(exp(as.numeric(x$coefficients)))))
   cmat <- cbind(cmat, as.vector(format(x$pleeq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreq)))
   cmat <- cbind(cmat, as.vector(format(x$pgreqabs)))
   colnames(cmat) <- c("Estimate", "Exp(b)", "Pr(<=b)", "Pr(>=b)", "Pr(>=|b|)")
   rownames(cmat)<- as.vector(x$names)
   print.table(cmat)
   cat("\nGoodness of Fit Statistics:\n")
   cat("\nNull deviance:",x$null.deviance,"on",x$df.null,"degrees of freedom\n")
   cat("Residual deviance:",x$deviance,"on",x$df.residual,"degrees of freedom\n")
   cat("Chi-Squared test of fit improvement:\n\t",x$null.deviance-x$deviance,"on",x$df.null-x$df.residual,"degrees of freedom, p-value",1-pchisq(x$null.deviance-x$deviance,df=x$df.null-x$df.residual),"\n") 
   cat("AIC:",x$aic,"\tBIC:",x$bic,"\nPseudo-R^2 Measures:\n\t(Dn-Dr)/(Dn-Dr+dfn):",(x$null.deviance-x$deviance)/(x$null.deviance-x$deviance+x$df.null),"\n\t(Dn-Dr)/Dn:",1-x$deviance/x$null.deviance,"\n")
   cat("Contingency Table (predicted (rows) x actual (cols)):\n\n")
   print.table(x$ctable,print.gap=3)
   cat("\n\tTotal Fraction Correct:",(x$ctable[1,1]+x$ctable[2,2])/sum(x$ctable),"\n\tFraction Predicted 1s Correct:",x$ctable[2,2]/sum(x$ctable[2,]),"\n\tFraction Predicted 0s Correct:",x$ctable[1,1]/sum(x$ctable[1,]),"\n")
   cat("\tFalse Negative Rate:",x$ctable[1,2]/sum(x$ctable[,2]),"\n")
   cat("\tFalse Positive Rate:",x$ctable[2,1]/sum(x$ctable[,1]),"\n")
   cat("\nTest Diagnostics:\n\n")
   cat("\tNull Hypothesis:",x$nullhyp,"\n")
   if(!is.null(x$dist)){
     cat("\tReplications:",dim(x$dist)[1],"\n")
     cat("\tDistribution Summary:\n\n")
     dmat<-apply(x$dist,2,min,na.rm=TRUE)
     dmat<-rbind(dmat,apply(x$dist,2,quantile,probs=0.25,names=FALSE, na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,quantile,probs=0.5,names=FALSE,na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,mean,na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,quantile,probs=0.75,names=FALSE, na.rm=TRUE))
     dmat<-rbind(dmat,apply(x$dist,2,max,na.rm=TRUE))
     colnames(dmat)<-as.vector(x$names)
     rownames(dmat)<-c("Min","1stQ","Median","Mean","3rdQ","Max")
     print.table(dmat,digits=4)
     cat("\n")
   }
}


#pstar - Perform an approximate p* analysis using the logistic regression 
#approximation.  Note that the result of this is returned as a GLM object, and 
#subsequent printing/summarizing/etc. should be treated accordingly.
pstar<-function(dat,effects=c("choice","mutuality","density","reciprocity","stransitivity","wtransitivity","stranstri","wtranstri","outdegree","indegree","betweenness","closeness","degcentralization","betcentralization","clocentralization","connectedness","hierarchy","lubness","efficiency"),attr=NULL,memb=NULL,diag=FALSE,mode="digraph"){
   #First, take care of various details
   dat<-as.sociomatrix.sna(dat)
   if(is.list(dat)||(is.array(dat)&&(length(dim(dat))>2)))
     stop("Single graphs required in pstar.")
   n<-dim(dat)[1]
   m<-dim(dat)[2]
   o<-list()
   #Next, add NAs as needed
   d<-dat
   if(!diag)
      d<-diag.remove(d)
   if(mode=="graph")
      d<-upper.tri.remove(d)
   #Make sure that attr and memb are well-behaved
   if(!is.null(attr)){
      if(is.vector(attr))
         attr<-matrix(attr,ncol=1)
      if(is.null(colnames(attr)))
         colnames(attr)<-paste("Attribute",1:dim(attr)[2],sep=".")
   }
   if(!is.null(memb)){
      if(is.vector(memb))
         memb<-matrix(memb,ncol=1)
      if(is.null(colnames(memb)))
         colnames(memb)<-paste("Membership",1:dim(memb)[2],sep=".")
   }
   #Now, evaluate each specified effect given each possible perturbation
   tiedat<-vector()
   for(i in 1:n)
      for(j in 1:m)
         if(!is.na(d[i,j])){
            #Assess the effects
            td<-vector()
            if(!is.na(pmatch("choice",effects))){  #Compute a choice effect
               td<-c(td,1)  #Always constant
            }
            if(!is.na(pmatch("mutuality",effects))){  #Compute a mutuality effect
               td<-c(td,eval.edgeperturbation(d,i,j,"mutuality"))
            }
            if(!is.na(pmatch("density",effects))){  #Compute a density effect
               td<-c(td,eval.edgeperturbation(d,i,j,"gden",mode=mode,diag=diag))
            }
            if(!is.na(pmatch("reciprocity",effects))){  #Compute a reciprocity effect
               td<-c(td,eval.edgeperturbation(d,i,j,"grecip"))
            }
            if(!is.na(pmatch("stransitivity",effects))){  #Compute a strong transitivity effect
               td<-c(td,eval.edgeperturbation(d,i,j,"gtrans",mode=mode,diag=diag,measure="strong"))
            }
            if(!is.na(pmatch("wtransitivity",effects))){  #Compute a weak transitivity effect
               td<-c(td,eval.edgeperturbation(d,i,j,"gtrans",mode=mode,diag=diag,measure="weak"))
            }
            if(!is.na(pmatch("stranstri",effects))){  #Compute a strong trans census effect
               td<-c(td,eval.edgeperturbation(d,i,j,"gtrans",mode=mode,diag=diag,measure="strongcensus"))
            }
            if(!is.na(pmatch("wtranstri",effects))){  #Compute a weak trans census effect
               td<-c(td,eval.edgeperturbation(d,i,j,"gtrans",mode=mode,diag=diag,measure="weakcensus"))
            }
            if(!is.na(pmatch("outdegree",effects))){  #Compute outdegree effects
               td<-c(td,eval.edgeperturbation(d,i,j,"degree",cmode="outdegree",gmode=mode,diag=diag))
            }
            if(!is.na(pmatch("indegree",effects))){  #Compute indegree effects
               td<-c(td,eval.edgeperturbation(d,i,j,"degree",cmode="indegree",gmode=mode,diag=diag))
            }
            if(!is.na(pmatch("betweenness",effects))){  #Compute betweenness effects
               td<-c(td,eval.edgeperturbation(d,i,j,"betweenness",gmode=mode,diag=diag))
            }
            if(!is.na(pmatch("closeness",effects))){  #Compute closeness effects
               td<-c(td,eval.edgeperturbation(d,i,j,"closeness",gmode=mode,diag=diag))
            }
            if(!is.na(pmatch("degcentralization",effects))){  #Compute degree centralization effects
               td<-c(td,eval.edgeperturbation(d,i,j,"centralization","degree",mode=mode,diag=diag))
            }
            if(!is.na(pmatch("betcentralization",effects))){  #Compute betweenness centralization effects
               td<-c(td,eval.edgeperturbation(d,i,j,"centralization","betweenness",mode=mode,diag=diag))
            }
            if(!is.na(pmatch("clocentralization",effects))){  #Compute closeness centralization effects
               td<-c(td,eval.edgeperturbation(d,i,j,"centralization","closeness",mode=mode,diag=diag))
            }
            if(!is.na(pmatch("connectedness",effects))){  #Compute connectedness effects
               td<-c(td,eval.edgeperturbation(d,i,j,"connectedness"))
            }
            if(!is.na(pmatch("hierarchy",effects))){  #Compute hierarchy effects
               td<-c(td,eval.edgeperturbation(d,i,j,"hierarchy"))
            }
            if(!is.na(pmatch("lubness",effects))){  #Compute lubness effects
               td<-c(td,eval.edgeperturbation(d,i,j,"lubness"))
            }
            if(!is.na(pmatch("efficiency",effects))){  #Compute efficiency effects
               td<-c(td,eval.edgeperturbation(d,i,j,"efficiency",diag=diag))
            }
            #Add attribute differences, if needed
            if(!is.null(attr))
               td<-c(td,abs(attr[i,]-attr[j,]))
            #Add membership similarities, if needed
            if(!is.null(memb))
               td<-c(td,as.numeric(memb[i,]==memb[j,]))
            #Add this data to the aggregated tie data
            tiedat<-rbind(tiedat,c(d[i,j],td))
         }
   #Label the tie data matrix
   tiedat.lab<-"EdgeVal"
   if(!is.na(pmatch("choice",effects)))  #Label the choice effect
      tiedat.lab<-c(tiedat.lab,"Choice")
   if(!is.na(pmatch("mutuality",effects)))  #Label the mutuality effect
      tiedat.lab<-c(tiedat.lab,"Mutuality")
   if(!is.na(pmatch("density",effects)))  #Label the density effect
      tiedat.lab<-c(tiedat.lab,"Density")
   if(!is.na(pmatch("reciprocity",effects)))  #Label the reciprocity effect
      tiedat.lab<-c(tiedat.lab,"Reciprocity")
   if(!is.na(pmatch("stransitivity",effects)))  #Label the strans effect
      tiedat.lab<-c(tiedat.lab,"STransitivity")
   if(!is.na(pmatch("wtransitivity",effects)))  #Label the wtrans effect
      tiedat.lab<-c(tiedat.lab,"WTransitivity")
   if(!is.na(pmatch("stranstri",effects)))  #Label the stranstri effect
      tiedat.lab<-c(tiedat.lab,"STransTriads")
   if(!is.na(pmatch("wtranstri",effects)))  #Label the wtranstri effect
      tiedat.lab<-c(tiedat.lab,"WTransTriads")
   if(!is.na(pmatch("outdegree",effects)))  #Label the outdegree effect
      tiedat.lab<-c(tiedat.lab,paste("Outdegree",1:n,sep="."))
   if(!is.na(pmatch("indegree",effects)))  #Label the indegree effect
      tiedat.lab<-c(tiedat.lab,paste("Indegree",1:n,sep="."))
   if(!is.na(pmatch("betweenness",effects)))  #Label the betweenness effect
      tiedat.lab<-c(tiedat.lab,paste("Betweenness",1:n,sep="."))
   if(!is.na(pmatch("closeness",effects)))  #Label the closeness effect
      tiedat.lab<-c(tiedat.lab,paste("Closeness",1:n,sep="."))
   if(!is.na(pmatch("degcent",effects)))  #Label the degree centralization effect
      tiedat.lab<-c(tiedat.lab,"DegCentralization")
   if(!is.na(pmatch("betcent",effects)))  #Label the betweenness centralization effect
      tiedat.lab<-c(tiedat.lab,"BetCentralization")
   if(!is.na(pmatch("clocent",effects)))  #Label the closeness centralization effect
      tiedat.lab<-c(tiedat.lab,"CloCentralization")
   if(!is.na(pmatch("connectedness",effects)))  #Label the connectedness effect
      tiedat.lab<-c(tiedat.lab,"Connectedness")
   if(!is.na(pmatch("hierarchy",effects)))  #Label the hierarchy effect
      tiedat.lab<-c(tiedat.lab,"Hierarchy")
   if(!is.na(pmatch("lubness",effects)))  #Label the lubness effect
      tiedat.lab<-c(tiedat.lab,"LUBness")
   if(!is.na(pmatch("efficiency",effects)))  #Label the efficiency effect
      tiedat.lab<-c(tiedat.lab,"Efficiency")
   if(!is.null(attr))
      tiedat.lab<-c(tiedat.lab,colnames(attr))
   if(!is.null(memb))
      tiedat.lab<-c(tiedat.lab,colnames(memb))
   colnames(tiedat)<-tiedat.lab
   #Having had our fun, it's time to get serious.  Run a GLM on the resulting data.
   fmla<-as.formula(paste("EdgeVal ~ -1 + ",paste(colnames(tiedat)[2:dim(tiedat)[2]],collapse=" + ")))
   o<-glm(fmla,family="binomial",data=as.data.frame(tiedat))
   o$tiedata<-tiedat
   #Return the result
   o
}


#se.lnam - Standard error method for lnam
se.lnam<-function(object, ...){
   se<-vector()
   sen<-vector()
   if(!is.null(object$beta.se)){
      se<-c(se,object$beta.se)
      sen<-c(sen,names(object$beta.se))
   }
   if(!is.null(object$rho1.se)){
      se<-c(se,object$rho1.se)
      sen<-c(sen,"rho1")
   }
   if(!is.null(object$rho2.se)){
      se<-c(se,object$rho2.se)
      sen<-c(sen,"rho2")
   }
   names(se)<-sen
   se
}


#summary.bayes.factor - A fairly generic summary routine for bayes factors.  
#Clearly, this belongs in some other library than sna, but for the moment this 
#will have to do...
summary.bayes.factor<-function(object, ...){
   o<-object
   rownames(o$int.lik)<-o$model.names
   colnames(o$int.lik)<-o$model.names
   o$inv.bf<--o$int.lik
   for(i in 1:dim(o$int.lik)[1])
      o$inv.bf[i,i]<-o$int.lik[i,i]-logSum(diag(o$int.lik))
   class(o)<-c("summary.bayes.factor","bayes.factor")
   o
}


#summary.bbnam - Summary method for bbnam
summary.bbnam<-function(object, ...){
   out<-object
   class(out)<-c("summary.bbnam",class(out))
   out
}


#summary.bbnam.actor - Summary method for bbnam.actor
summary.bbnam.actor<-function(object, ...){
   out<-object
   class(out)<-c("summary.bbnam.actor",class(out))
   out
}


#summary.bbnam.fixed - Summary method for bbnam.fixed
summary.bbnam.fixed<-function(object, ...){
   out<-object
   class(out)<-c("summary.bbnam.fixed",class(out))
   out
}


#summary.bbnam.pooled - Summary method for bbnam.pooled
summary.bbnam.pooled<-function(object, ...){
   out<-object
   class(out)<-c("summary.bbnam.pooled",class(out))
   out
}


#summary.bn - Summary method for bn
summary.bn<-function(object, ...){
  out<-object
  class(out)<-c("summary.bn",class(out))
  out
}


#summary.brokerage - Summary method for brokerage objects
summary.brokerage<-function(object,...){
  class(object)<-"summary.brokerage"
  object
}


#summary.lnam - Summary method lnam
summary.lnam<-function(object, ...){
   ans<-object
   class(ans)<-c("summary.lnam","lnam")
   ans
}


#summary.netcancor - Summary method for netcancor
summary.netcancor<-function(object, ...){
   out<-object
   class(out)<-c("summary.netcancor",class(out))
   out
}


#summary.netlm - Summary method for netlm
summary.netlm<-function(object, ...){
   out<-object
   class(out)<-c("summary.netlm",class(out))
   out
}


#summary.netlogit - Summary method for netlogit
summary.netlogit<-function(object, ...){
   out<-object
   class(out)<-c("summary.netlogit",class(out))
   out
}

Try the sna package in your browser

Any scripts or data that you put into this service are public.

sna documentation built on Sept. 11, 2024, 8:45 p.m.