docs: def kronecker_lmm(snps,phenos,covs=None,Acovs=None,Asnps=None,K1r=None,K1c=None,K2r=None,K2c=None,covar_type='lowrank_diag',rank=1,NumIntervalsDelta0=100,NumIntervalsDeltaAlt=0,searchDelta=False):
      """
      simple wrapper for kroneckerLMM code
  
      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].
          Asnps:          single SP.array of I0 interaction variables to be included in the
                          background model when testing for interaction with Inters
                          If not provided, the alternative model will be the independent model
          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:    Boolean indicator if delta is optimized during SNP testing (default False)
  
      Returns:
          CKroneckerLMM object
          P-values for all SNPs from liklelihood ratio test
      """
      #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 Asnps is None:
          Asnps = [SP.ones([1,P])]
      if (type(Asnps)!=list):
          Asnps = [Asnps]
      assert len(Asnps)>0, "need at least one Snp design matrix"
  
      #one row per column design matrix
      pv = SP.zeros((len(Asnps),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
  
      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)
      else:
          lmm.setNumIntervalsAlt(0)
      lmm.setNumIntervals0(NumIntervalsDelta0)
  
      for iA in range(len(Asnps)):
          #add SNP design
          lmm.setSNPcoldesign(Asnps[iA])
          lmm.process()
          pv[iA,:] = lmm.getPv()[0]
      return lmm,pv