docs: public void update(final List<Vec> docs, ExecutorService ex)
      {
          //need to init structure?
          if(lambda == null)
              initialize();
          /*
           * Make sure the beta values we will need are up to date
           */
          updateBetas(docs, ex);
          
          /*
           * Note, on each update we dont modify or access lambda untill the very,
           * end -  so we can interleave the "M" step into the final update 
           * accumulation to avoid temp space allocation and more easily exploit 
           * sparsity
           * 
           */
          
          
          //2: Set the step-size schedule ρt appropriately.
          final double rho_t = Math.pow(tau0+(t++), -kappa);
          
          
          //pre-shrink the lambda values so we can add out updates later
          for(int k = 0; k < K; k++)
          {
              lambda.get(k).mutableMultiply(1-rho_t);
              lambdaSums.set(k, lambdaSums.getD(k)*(1-rho_t));
          }
          
          /*
           * As described in the 2010 paper, this part is the "E" step if we view
           * it as an EM algorithm
           */
          
          //5: Initialize γ_dk =1, for k ∈ {1, . . . ,K}.
          
          /*
           * See note on page 3 from 2010 paper: "In practice, this algorithm 
           * converges to a better solution if we reinitialize γ and φ before
           * each E step"
           */
          final int P = SystemInfo.LogicalCores;
          final CountDownLatch latch = new CountDownLatch(P);
          //main iner loop, outer is per document and inner most is per topic convergence
          for(int id = 0; id < P; id++)
          {
              final int ID = id;
              ex.submit(new Runnable()
              {
                  @Override
                  public void run()
                  {
                      Random rand = RandomUtil.getRandom();
                      for(int d = ParallelUtils.getStartBlock(docs.size(), ID, P); d < ParallelUtils.getEndBlock(docs.size(), ID, P); d++)
                      {
                          final Vec doc = docs.get(d);
                          if(doc.nnz() == 0)
                              continue;
                          final Vec ELogTheta_d = logThetaLocal.get();
                          final Vec ExpELogTheta_d = expLogThetaLocal.get();
                          final Vec gamma_d = gammaLocal.get();
  
                          /*
                           * Make sure gamma and theta are set up and ready to start iterating 
                           */
                          prepareGammaTheta(gamma_d, ELogTheta_d, ExpELogTheta_d, rand);
  
                          int[] indexMap = new int[doc.nnz()];
                          double[] phiCols = new double[doc.nnz()];
  
                          //φ^k_dn ∝ exp{E[logθdk]+E[logβk,wdn ]}, k ∈ {1, . . . ,K}
                          computePhi(doc, indexMap, phiCols, K, gamma_d, ELogTheta_d, ExpELogTheta_d);
  
                          //accumulate updates, the "M" step
                          IntList toUpdate = new IntList(K);
                          ListUtils.addRange(toUpdate, 0, K, 1);
                          Collections.shuffle(toUpdate, rand);//helps reduce contention caused by shared iteration order
                          int updatePos = 0;
                          while(!toUpdate.isEmpty())
                          {
                              int k = toUpdate.getI(updatePos);
                              
                              if(lambdaLocks.get(k).tryLock())
                              {
                                  final double coeff = ExpELogTheta_d.get(k)*rho_t*D/docs.size();
                                  final Vec lambda_k = lambda.get(k);
                                  final Vec ExpELogBeta_k = ExpELogBeta.get(k);
                                  double lambdaSum_k = lambdaSums.getD(k);
                                  
                                  /*
                                   * iterate and incremebt ourselves so that we can also compute 
                                   * the new sums in 1 pass
                                   */
                                  for(int i = 0; i < doc.nnz(); i++)
                                  {
                                      int indx = indexMap[i];
                                      double toAdd = coeff*phiCols[i]*ExpELogBeta_k.get(indx);
                                      lambda_k.increment(indx, toAdd);
                                      lambdaSum_k += toAdd;
                                  }
  
                                  lambdaSums.set(k, lambdaSum_k);
                                  lambdaLocks.get(k).unlock();
                                  
                                  toUpdate.remove(updatePos);
                              }
                              
                              if(!toUpdate.isEmpty())
                                  updatePos = (updatePos+1) % toUpdate.size();
                          }
                      }
                      
                      latch.countDown();
                  }
              });
              
          }
         
          try
          {
              latch.await();
          }
          catch (InterruptedException ex1)
          {
              Logger.getLogger(OnlineLDAsvi.class.getName()).log(Level.SEVERE, null, ex1);
          }
      }