docs: def simple_interaction_kronecker(snps,phenos,covs=None,Acovs=None,Asnps1=None,Asnps0=None,K1r=None,K1c=None,K2r=None,K2c=None,covar_type='lowrank_diag',rank=1,NumIntervalsDelta0=100,NumIntervalsDeltaAlt=0,searchDelta=False):
      """
      I-variate fixed effects interaction test for phenotype specific SNP effects
  
      Args:
          snps:   [N x S] SP.array of S SNPs for N individuals (test SNPs)
          phenos: [N x P] SP.array of P phenotypes for N individuals
          covs:           list of SP.arrays holding covariates. Each covs[i] has one corresponding Acovs[i]
          Acovs:          list of SP.arrays holding the phenotype design matrices for covariates.
                          Each covs[i] has one corresponding Acovs[i].
          Asnps1:         list of SP.arrays of I interaction variables to be tested for N
                          individuals. Note that it is assumed that Asnps0 is already included.
                          If not provided, the alternative model will be the independent model
          Asnps0:         single SP.array of I0 interaction variables to be included in the
                          background model when testing for interaction with Inters
          K1r:    [N x N] SP.array of LMM-covariance/kinship koefficients (optional)
                          If not provided, then linear regression analysis is performed
          K1c:    [P x P] SP.array of LMM-covariance/kinship koefficients (optional)
                          If not provided, then linear regression analysis is performed
          K2r:    [N x N] SP.array of LMM-covariance/kinship koefficients (optional)
                          If not provided, then linear regression analysis is performed
          K2c:    [P x P] SP.array of LMM-covariance/kinship koefficients (optional)
                          If not provided, then linear regression analysis is performed
          covar_type:     type of covaraince to use. Default 'freeform'. possible values are
                          'freeform': free form optimization,
                          'fixed': use a fixed matrix specified in covar_K0,
                          'diag': optimize a diagonal matrix,
                          'lowrank': optimize a low rank matrix. The rank of the lowrank part is specified in the variable rank,
                          'lowrank_id': optimize a low rank matrix plus the weight of a constant diagonal matrix. The rank of the lowrank part is specified in the variable rank,
                          'lowrank_diag': optimize a low rank matrix plus a free diagonal matrix. The rank of the lowrank part is specified in the variable rank,
                          'block': optimize the weight of a constant P x P block matrix of ones,
                          'block_id': optimize the weight of a constant P x P block matrix of ones plus the weight of a constant diagonal matrix,
                          'block_diag': optimize the weight of a constant P x P block matrix of ones plus a free diagonal matrix,
          rank:           rank of a possible lowrank component (default 1)
          NumIntervalsDelta0:  number of steps for delta optimization on the null model (100)
          NumIntervalsDeltaAlt:number of steps for delta optimization on the alt. model (0 - no optimization)
          searchDelta:     Carry out delta optimization on the alternative model? if yes We use NumIntervalsDeltaAlt steps
      Returns:
          pv:     P-values of the interaction test
          pv0:    P-values of the null model
          pvAlt:  P-values of the alternative model
      """
      S=snps.shape[1]
      #0. checks
      N  = phenos.shape[0]
      P  = phenos.shape[1]
  
      if K1r==None:
          K1r = SP.dot(snps,snps.T)
      else:
          assert K1r.shape[0]==N, 'K1r: dimensions dismatch'
          assert K1r.shape[1]==N, 'K1r: dimensions dismatch'
  
      if K2r==None:
          K2r = SP.eye(N)
      else:
          assert K2r.shape[0]==N, 'K2r: dimensions dismatch'
          assert K2r.shape[1]==N, 'K2r: dimensions dismatch'
  
      covs,Acovs = updateKronCovs(covs,Acovs,N,P)
  
      #Asnps can be several designs
      if (Asnps0 is None):
          Asnps0 = [SP.ones([1,P])]
      if Asnps1 is None:
          Asnps1 = [SP.eye([P])]
      if (type(Asnps0)!=list):
          Asnps0 = [Asnps0]
      if (type(Asnps1)!=list):
          Asnps1 = [Asnps1]
      assert (len(Asnps0)==1) and (len(Asnps1)>0), "need at least one Snp design matrix for null and alt model"
  
      #one row per column design matrix
      pv = SP.zeros((len(Asnps1),snps.shape[1]))
      lrt = SP.zeros((len(Asnps1),snps.shape[1]))
      pvAlt = SP.zeros((len(Asnps1),snps.shape[1]))
      lrtAlt = SP.zeros((len(Asnps1),snps.shape[1]))
  
      #1. run GP model to infer suitable covariance structure
      if K1c==None or K2c==None:
          vc = estimateKronCovariances(phenos=phenos, K1r=K1r, K2r=K2r, K1c=K1c, K2c=K2c, covs=covs, Acovs=Acovs, covar_type=covar_type, rank=rank)
          K1c = vc.getEstTraitCovar(0)
          K2c = vc.getEstTraitCovar(1)
      else:
          assert K1c.shape[0]==P, 'K1c: dimensions dismatch'
          assert K1c.shape[1]==P, 'K1c: dimensions dismatch'
          assert K2c.shape[0]==P, 'K2c: dimensions dismatch'
          assert K2c.shape[1]==P, 'K2c: dimensions dismatch'
  
      #2. run kroneckerLMM for null model
      lmm = limix.CKroneckerLMM()
      lmm.setK1r(K1r)
      lmm.setK1c(K1c)
      lmm.setK2r(K2r)
      lmm.setK2c(K2c)
      lmm.setSNPs(snps)
      #add covariates
      for ic  in range(len(Acovs)):
          lmm.addCovariates(covs[ic],Acovs[ic])
      lmm.setPheno(phenos)
  
      #delta serch on alt. model?
      if searchDelta:
          lmm.setNumIntervalsAlt(NumIntervalsDeltaAlt)
          lmm.setNumIntervals0_inter(NumIntervalsDeltaAlt)
      else:
          lmm.setNumIntervalsAlt(0)
          lmm.setNumIntervals0_inter(0)
  
  
      lmm.setNumIntervals0(NumIntervalsDelta0)
      #add SNP design
      lmm.setSNPcoldesign0_inter(Asnps0[0])
      for iA in range(len(Asnps1)):
          lmm.setSNPcoldesign(Asnps1[iA])
          lmm.process()
  
          pvAlt[iA,:] = lmm.getPv()[0]
          pv[iA,:] = lmm.getPv()[1]
          pv0 = lmm.getPv()[2]
      return pv,pv0,pvAlt