docs: def _sample(self, n_samples):
          """
      Sample
          Generate samples for posterior distribution using Gauss-Newton 
          proposal parameters
      Inputs : 
          n_samples :
              number of samples to generate
      Hidden Outputs :
          chain  :
              chain of samples
          n_samples :
              length of chain
          n_accepted :
              number of proposals accepted
          step_count :
              count of the steps accepted
          """
          try : 
              n_samples = int(n_samples)
          except :
              raise TypeError("number of samples has to be an integer")
              exit()
  
          # fetch info
          X = self._proposal_params(self._X)
          k_max = self._max_steps
  
          # initialize 
          chain = np.zeros((n_samples, self._n)) 
          n_accepted = 0
          step_count = np.zeros(k_max+2)
  
          # begin outer loop
          for i in xrange(n_samples):
              accepted  = False       # check if sample is accepted
              r_        = [1]         # list of step sizes
              Z_        = [X]         # initialize list of Z s
              self._r_  = r_ 
              log_P_z_x = 0. + X['log_p'] 
  
              k = 0 # back-off steps taken so far
              while k <= k_max:
                  # get proposal
                  chi_z = False
                  while not chi_z:
                      z = multi_normal(X, r_[-1])
                      chi_z, f_z, J_z = self._f(z)
                  Z = self._proposal_params({'x':z,'f':f_z,'J':J_z})
                  Z_.append(Z)
                  self._Z_ = Z_
  
                  log_P_z_x += log_K(Z, X, r_[-1])
  
                  # N is the Numerator of the acceptance, N = P_x_z
                  self._N_is_0 = False # check to see if N = 0, to use in _log_P
                  log_N = self._log_P(X, Z, k)
  
                  # calculating acceptance probability
                  if self._N_is_0 == True :
                      A_z_x = 0.
                  elif log_N >= log_P_z_x :
                      A_z_x = 1.
                  else :
                      A_z_x = np.exp(log_N - log_P_z_x)
  
                  # acceptance rejection
                  if  np.random.rand() <= A_z_x:
                      accepted = True
                      break
                  else : 
                      log_P_z_x += np.log(1. - A_z_x)
                      self._back_off()
                      k += 1                     
              # end of steps for loop
              if accepted == True :
                  chain[i,:] = z[:,0] 
                  X = Z
                  # for statistics
                  n_accepted += 1    
                  step_count[k+1] += 1
              else :
                  chain[i,:]  = X['x'][:,0]
                  # for statistics
                  step_count[0] += 1
          # end outer loop
  
          # update stored info
          self._X = X
  
          # outputs
          if self._n_samples == 0 :
              self._chain = chain
              self._step_count = step_count
          else :
              self._chain = np.append(self._chain, chain, axis=0)
              self._step_count = np.add(self._step_count, step_count)
          self._n_samples += n_samples
          self._n_accepted += n_accepted