// O(1) if up to date, O(M^2) if epsilon new, O(M^3) if Sigma new
VectorXd MultivariateFNormalSufficient::evaluate_derivative_FM() const
{
      // d(-log(p))/d(FM) = - N * P * epsilon
      LOG( "MVN: evaluate_derivative_FM() = " << std::endl);
      VectorXd tmp(-N_ * get_Peps()/SQUARE(factor_));
      return tmp;
}
  MatrixXd MultivariateFNormalSufficient::compute_PTP() const
{
  LOG( "MVN:   computing PTP" << std::endl);
  VectorXd peps(get_Peps());
  MatrixXd tmp(peps*peps.transpose());
  return tmp;
}
 MatrixXd MultivariateFNormalSufficient::evaluate_second_derivative_FM_Sigma(
         unsigned k) const
 {
     CHECK(N_==1, "not implemented when N>1");
     MatrixXd P(get_P());
     VectorXd Peps(get_Peps());
     MatrixXd ret(P.transpose().col(k)*Peps.transpose());
     return ret/SQUARE(factor_);
 }
  MatrixXd MultivariateFNormalSufficient::compute_PTP() const
{
  timer_.start(PTP);
  IMP_LOG(TERSE, "MVN:   computing PTP" << std::endl);
  VectorXd peps(get_Peps());
  MatrixXd tmp(peps*peps.transpose());
  timer_.stop(PTP);
  return tmp;
}
 MatrixXd MultivariateFNormalSufficient::evaluate_second_derivative_FM_Sigma(
         unsigned k) const
 {
     if (N_!=1) IMP_THROW("not implemented when N>1", ModelException);
     MatrixXd P(get_P());
     VectorXd Peps(get_Peps());
     MatrixXd ret(P.transpose().col(k)*Peps.transpose());
     return ret/IMP::square(factor_);
 }
  // O(1) if up to date, O(M^2) if epsilon new, O(M^3) if Sigma new
VectorXd MultivariateFNormalSufficient::evaluate_derivative_FM() const
{
      timer_.start(DFM);
      // d(-log(p))/d(FM) = - N * P * epsilon
      IMP_LOG(TERSE, "MVN: evaluate_derivative_FM() = " << std::endl);
      VectorXd tmp(-N_ * get_Peps()/IMP::square(factor_));
      timer_.stop(DFM);
      return tmp;
}
  double MultivariateFNormalSufficient::get_mean_square_residuals() const
{
    //std::cout << "P " << std::endl << P_ << std::endl;
    //std::cout << "epsilon " << std::endl << get_epsilon() << std::endl;
    VectorXd Peps(get_Peps());
    VectorXd epsilon(get_epsilon());
    double dist = epsilon.transpose()*Peps;
    LOG( "MVN:   get_mean_square_residuals = " << dist << std::endl);
    return dist;
}
MatrixXd MultivariateFNormalSufficient::evaluate_second_derivative_Sigma_Sigma(
        unsigned m, unsigned n) const
  {
      CHECK(N_==1, "not implemented when N>1");
      MatrixXd P(get_P());
      VectorXd Peps(get_Peps());
      MatrixXd tmp(P.col(m)*Peps.transpose());
      MatrixXd ret(0.5*(-P.col(n)*P.row(m)
                        +Peps(n)*(tmp+tmp.transpose())));
      return ret/SQUARE(factor_);
  }
MatrixXd MultivariateFNormalSufficient::evaluate_second_derivative_Sigma_Sigma(
        unsigned m, unsigned n) const
  {
      if (N_!=1) IMP_THROW("not implemented when N>1", ModelException);
      MatrixXd P(get_P());
      VectorXd Peps(get_Peps());
      MatrixXd tmp(P.col(m)*Peps.transpose());
      MatrixXd ret(0.5*(-P.col(n)*P.row(m)
                        +Peps(n)*(tmp+tmp.transpose())));
      return ret/IMP::square(factor_);
  }