docs: public SufficientStatistics train(ParametricFactorGraph bn, SufficientStatistics initialParameters,
        Iterable<Example<DynamicAssignment, DynamicAssignment>> trainingData) {
      // Initialize state variables, which are used in updateBatchStatistics() and
      // updateParameters()
      double totalDecay = 1.0;
      // Make sure that the training data list is safe for concurrent access.
      List<DynamicAssignment> trainingDataList = getOutputAssignments(trainingData, true);
      int numUpdates = 0;
  
      Collections.shuffle(trainingDataList);
  
      for (int i = 0; i < numIterations; i++) {
        log.notifyIterationStart(i);
  
        int numBatches = (int) Math.ceil(((double) trainingDataList.size()) / batchSize);
        for (int j = 0; j < numBatches; j++) {
          List<DynamicAssignment> batch = trainingDataList.subList(j * batchSize,
              Math.min((j + 1) * batchSize, trainingDataList.size()));
  
          // Calculate the sufficient statistics for batch.
          DynamicFactorGraph factorGraph = bn.getModelFromParameters(initialParameters);
          SufficientStatisticsBatch result = MapReduceConfiguration.getMapReduceExecutor()
              .mapReduce(batch,
                  new SufficientStatisticsMapper(factorGraph, marginalCalculator, log),
                  new SufficientStatisticsReducer(bn, initialParameters));
          SufficientStatistics batchStatistics = result.getStatistics();
          log.logStatistic(i, "average loglikelihood",
              result.getLoglikelihood() / result.getNumExamples());
  
          // Update the the parameter vector.
          // Instead of multiplying the sufficient statistics (dense update)
          // use a sparse update which simply increases the weight of the added
          // marginals.
          double batchDecayParam = Math.pow((numUpdates + 2), -1.0 * decayRate);
          double newTotalDecay = totalDecay * (1.0 - batchDecayParam);
          double batchMultiplier = batchDecayParam / newTotalDecay;
  
          initialParameters.increment(batchStatistics, batchMultiplier);
  
          totalDecay = newTotalDecay;
          numUpdates++;
        }
  
        log.notifyIterationEnd(i);
      }
      return initialParameters;
    }