hes_random_obj.cpp

View page source

Hessian of Random Effects Objective: Example and Test

# include <cppad/cppad.hpp>
# include <cppad/mixed/cppad_mixed.hpp>


namespace {
   using CppAD::log;
   using CppAD::AD;
   //
   using CppAD::mixed::d_sparse_rcv;
   using CppAD::mixed::d_vector;
   using CppAD::mixed::a1_vector;
   using CppAD::mixed::s_vector;
   //
   class mixed_derived : public cppad_mixed {
   private:
      const d_vector&       y_;
   public:
      // constructor
      mixed_derived(
         size_t                 n_fixed       ,
         size_t                 n_random      ,
         bool                   quasi_fixed   ,
         bool                   bool_sparsity ,
         const d_vector&       y              ) :
         cppad_mixed(n_fixed, n_random, quasi_fixed, bool_sparsity) ,
         y_(y)
      { }
      // implementation of ran_likelihood
      a1_vector ran_likelihood(
         const a1_vector&         theta  ,
         const a1_vector&         u      ) override
      {
         a1_vector vec(1);

         // compute this factor once
         a1_double sqrt_2pi =  CppAD::sqrt( 8.0 * CppAD::atan(1.0) );

         // initialize summation
         vec[0] = 0.0;

         // for each data and random effect
         for(size_t i = 0; i < y_.size(); i++)
         {  a1_double mu     = u[i];
            a1_double sigma  = theta[i] + double(i) / double( y_.size() );
            a1_double res    = (y_[i] - mu) / sigma;

            // This is a Gaussian term, so entire density is smooth
            vec[0]  += log(sqrt_2pi * sigma) + res * res / 2.0;
         }
         return vec;
      }
   };
}

bool hes_random_obj_xam(void)
{
   bool   ok  = true;
   double eps = 100. * std::numeric_limits<double>::epsilon();
   //
   size_t n_data   = 10;
   size_t n_fixed  = n_data;
   size_t n_random = n_data;
   d_vector  data(n_data);
   d_vector  fixed_vec(n_fixed), random_vec(n_random);
   a1_vector a1_fixed(n_fixed), a1_random(n_random);

   for(size_t i = 0; i < n_data; i++)
   {  data[i]       = double(i + 1);
      //
      fixed_vec[i]  = 1.5;
      a1_fixed[i]   = fixed_vec[i];
      //
      random_vec[i] = 0.0;
      a1_random[i]  = random_vec[i];
   }

   // object that is derived from cppad_mixed
   bool quasi_fixed  = true;
   bool bool_sparsity = true;
   mixed_derived mixed_object(
      n_fixed, n_random, quasi_fixed, bool_sparsity, data
   );
   mixed_object.initialize(fixed_vec, random_vec);

   // Evaluate Hessian of random likelihood w.r.t random effects
   d_sparse_rcv hes_random_obj_rcv =
      mixed_object.hes_random_obj(fixed_vec, random_vec);

   // check the Hessian
   ok &= hes_random_obj_rcv.nnz() == n_data;

   for(size_t k = 0; k < n_data; ++k)
   {  size_t r     = hes_random_obj_rcv.row()[k];
      size_t c     = hes_random_obj_rcv.col()[k];
      double v     = hes_random_obj_rcv.val()[k];
      ok          &= r == c;
      double sigma = fixed_vec[r] + double(r) / double(n_data);
      double check = 1.0 / (sigma * sigma);
      ok          &= fabs( check / v - 1.0 ) < eps;
   }
   return ok;
}