61  static const std::vector<double> ACC_MATRIX_DIAGONAL_VALUES = { -1.0 / 12.0, 16.0 / 12.0, -30.0 / 12.0, 16.0 / 12.0,
 
   63  static const std::vector<int> ACC_MATRIX_DIAGONAL_INDICES = { -2, -1, 0, 1, 2 };
 
   65  auto fill_diagonal = [](Eigen::MatrixXd& m, 
double coeff, 
int diag_index) {
 
   66    std::size_t size = m.rows() - std::abs(diag_index);
 
   67    m.diagonal(diag_index) = Eigen::VectorXd::Constant(size, coeff);
 
   71  Eigen::MatrixXd acceleration = Eigen::MatrixXd::Zero(num_timesteps, num_timesteps);
 
   72  for (
auto i = 0u; i < ACC_MATRIX_DIAGONAL_INDICES.size(); i++)
 
   74    fill_diagonal(acceleration, ACC_MATRIX_DIAGONAL_VALUES[i], ACC_MATRIX_DIAGONAL_INDICES[i]);
 
   78  Eigen::MatrixXd covariance = Eigen::MatrixXd::Identity(num_timesteps, num_timesteps);
 
   79  covariance = acceleration.transpose() * acceleration;
 
   80  covariance = covariance.fullPivLu().inverse();
 
   81  covariance /= covariance.array().abs().matrix().maxCoeff();
 
   84  std::vector<math::MultivariateGaussianPtr> rand_generators(stddev.size());
 
   85  for (
auto& r : rand_generators)
 
   87    r = std::make_shared<math::MultivariateGaussian>(Eigen::VectorXd::Zero(num_timesteps), covariance);
 
   90  auto raw_noise = std::make_shared<Eigen::VectorXd>(num_timesteps);
 
   91  NoiseGeneratorFn noise_generator_fn = [=](
const Eigen::MatrixXd& values, Eigen::MatrixXd& noisy_values,
 
   92                                            Eigen::MatrixXd& noise) {
 
   93    for (
int i = 0; i < values.rows(); ++i)
 
   95      rand_generators[i]->sample(*raw_noise);
 
   96      raw_noise->head(1).setZero();
 
   97      raw_noise->tail(1).setZero();  
 
   98      noise.row(i).transpose() = stddev.at(i) * (*raw_noise);
 
   99      noisy_values.row(i) = values.row(i) + noise.row(i);
 
  103  return noise_generator_fn;