code: def black_box_variational_inference(logprob, D, num_samples):
      
  
      def unpack_params(params):
          # Variational dist is a diagonal Gaussian.
          mean, log_std = params[:D], params[D:]
          return mean, log_std
  
      def gaussian_entropy(log_std):
          return 0.5 * D * (1.0 + np.log(2*np.pi)) + np.sum(log_std)
  
      rs = npr.RandomState(0)
      def variational_objective(params, t):
          """Provides a stochastic estimate of the variational lower bound."""
          mean, log_std = unpack_params(params)
          samples = rs.randn(num_samples, D) * np.exp(log_std) + mean
          lower_bound = gaussian_entropy(log_std) + np.mean(logprob(samples, t))
          return -lower_bound
  
      gradient = grad(variational_objective)
  
      return variational_objective, gradient, unpack_params
docs: Implements http://arxiv.org/abs/1401.0118, and uses the
      local reparameterization trick from http://arxiv.org/abs/1506.02557