import torch
import math


def importance_sampling_cross_validation(logp):
    """Compute the importance-sampling cross validation (ISCV) estimate.

    The ISCV estimates the holdout log-likelihood from just an approximation to
    the posterior predictive log-likelihoods on the training data.

    ### References:
    [1]: Alan E. Gelfand, Dipak K. Dey, Hong Chang.
      Model determination using predictive distributions with implementation via
      sampling-based methods.
      Technical report No. 462, Department of Statistics,
      Stanford university, 1992.
      http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.860.3702&rep=rep1&type=pdf
    [2]: Aki Vehtari, Andrew Gelman, Jonah Gabry.
      Practical Bayesian model evaluation using leave-one-out cross-validation and
      WAIC.
      arXiv:1507.04544
      https://arxiv.org/pdf/1507.04544.pdf
    [3]: Sumio Watanabe. Mathematical Theory of Bayesian Statistics.
      CRC Press. 2018
      https://www.crcpress.com/Mathematical-Theory-of-Bayesian-Statistics/Watanabe/p/book/9781482238068


    Args:
      logp: Tensor, shape (B,M,...), containing log p(y_i | x_i, theta_j)
        for i=1,..,B instances and j=1,...,M models.

    Returns:
      iscv_logp: Tensor, (...), the ISCV estimate of the holdout log-likelihood.
      iscv_logp_sem: Tensor, (...), the standard error of th emean of `iscv_logp`.
    """
    logse = torch.logsumexp(-logp, 1)
    iscv_logp = logse.mean(0)
    iscv_logp_var = logse.std(0)
    m = int(logp.shape[1])
    iscv_logp -= math.log(m)
    iscv_logp = -iscv_logp
    iscv_logp_sem = (iscv_logp_var / float(m)).sqrt()

    return iscv_logp, iscv_logp_sem