class_docstr: Poincaré Variational Autoencoder model.
  
  Args:
      model_config (PoincareVAEConfig): The Poincaré Variational Autoencoder configuration
          setting the main parameters of the model.
  
      encoder (BaseEncoder): An instance of BaseEncoder (inheriting from `torch.nn.Module` which
          plays the role of encoder. This argument allows you to use your own neural networks
          architectures if desired. If None is provided, a simple Multi Layer Preception
          (https://en.wikipedia.org/wiki/Multilayer_perceptron) is used. Default: None.
  
      decoder (BaseDecoder): An instance of BaseDecoder (inheriting from `torch.nn.Module` which
          plays the role of decoder. This argument allows you to use your own neural networks
          architectures if desired. If None is provided, a simple Multi Layer Preception
          (https://en.wikipedia.org/wiki/Multilayer_perceptron) is used. Default: None.
  
  .. note::
      For high dimensional data we advice you to provide you own network architectures. With the
      provided MLP you may end up with a ``MemoryError``.
class_docstr_tok: ["Poincar\u00e9","Variational","Autoencoder","model",".","Args",":","model_config","(","PoincareVAEConfig",")",":","The","Poincar\u00e9","Variational","Autoencoder","configuration","setting","the","main","parameters","of","the","model",".","encoder","(","BaseEncoder",")",":","An","instance","of","BaseEncoder","(","inheriting","from","`","torch.nn.Module","`","which","plays","the","role","of","encoder",".","This","argument","allows","you","to","use","your","own","neural","networks","architectures","if","desired",".","If","None","is","provided",",","a","simple","Multi","Layer","Preception","(","https",":","\/\/en.wikipedia.org\/wiki\/Multilayer_perceptron",")","is","used",".","Default",":","None",".","decoder","(","BaseDecoder",")",":","An","instance","of","BaseDecoder","(","inheriting","from","`","torch.nn.Module","`","which","plays","the","role","of","decoder",".","This","argument","allows","you","to","use","your","own","neural","networks","architectures","if","desired",".","If","None","is","provided",",","a","simple","Multi","Layer","Preception","(","https",":","\/\/en.wikipedia.org\/wiki\/Multilayer_perceptron",")","is","used",".","Default",":","None",".","..","note",":",":","For","high","dimensional","data","we","advice","you","to","provide","you","own","network","architectures",".","With","the","provided","MLP","you","may","end","up","with","a","``","MemoryError","``","."]
docstr: Function computed the estimate negative log-likelihood of the model. It uses importance
  sampling method with the approximate posterior distribution. This may take a while.
  
  Args:
      data (torch.Tensor): The input data from which the log-likelihood should be estimated.
          Data must be of shape [Batch x n_channels x ...]
      n_samples (int): The number of importance samples to use for estimation
      batch_size (int): The batchsize to use to avoid memory issues
docstr_tok: ["Function","computed","the","estimate","negative","log-likelihood","of","the","model",".","It","uses","importance","sampling","method","with","the","approximate","posterior","distribution",".","This","may","take","a","while",".","Args",":","data","(","torch.Tensor",")",":","The","input","data","from","which","the","log-likelihood","should","be","estimated",".","Data","must","be","of","shape","[","Batch","x","n_channels","x","...","]","n_samples","(","int",")",":","The","number","of","importance","samples","to","use","for","estimation","batch_size","(","int",")",":","The","batchsize","to","use","to","avoid","memory","issues"]
code: def get_nll(self, data, n_samples=1, batch_size=100):
      """
      Function computed the estimate negative log-likelihood of the model. It uses importance
      sampling method with the approximate posterior distribution. This may take a while.
  
      Args:
          data (torch.Tensor): The input data from which the log-likelihood should be estimated.
              Data must be of shape [Batch x n_channels x ...]
          n_samples (int): The number of importance samples to use for estimation
          batch_size (int): The batchsize to use to avoid memory issues
      """
  
      if n_samples <= batch_size:
          n_full_batch = 1
      else:
          n_full_batch = n_samples // batch_size
          n_samples = batch_size
  
      log_p = []
  
      for i in range(len(data)):
          x = data[i].unsqueeze(0)
  
          log_p_x = []
  
          for j in range(n_full_batch):
              x_rep = torch.cat(batch_size * [x])
  
              encoder_output = self.encoder(x_rep)
              if (
                  self.model_config.posterior_distribution
                  == "riemannian_normal"
              ):
                  mu, log_var = (
                      encoder_output.embedding,
                      encoder_output.log_concentration,
                  )
              else:
                  mu, log_var = (
                      encoder_output.embedding,
                      encoder_output.log_covariance,
                  )
  
              std = torch.exp(0.5 * log_var)
  
              qz_x = self.posterior(
                  loc=mu, scale=std, manifold=self.latent_manifold
              )
              z = qz_x.rsample(torch.Size([1]))
  
              pz = self.prior(
                  loc=self._pz_mu,
                  scale=self._pz_logvar.exp(),
                  manifold=self.latent_manifold,
              )
  
              log_q_z_given_x = qz_x.log_prob(z).sum(-1).squeeze(0)
              log_p_z = pz.log_prob(z).sum(-1).squeeze(0)
  
              recon_x = self.decoder(z.squeeze(0))["reconstruction"]
  
              if self.model_config.reconstruction_loss == "mse":
                  log_p_x_given_z = -0.5 * F.mse_loss(
                      recon_x.reshape(x_rep.shape[0], -1),
                      x_rep.reshape(x_rep.shape[0], -1),
                      reduction="none",
                  ).sum(dim=-1) - torch.tensor(
                      [np.prod(self.input_dim) / 2 * np.log(np.pi * 2)]
                  ).to(
                      data.device
                  )  # decoding distribution is assumed unit variance  N(mu, I)
  
              elif self.model_config.reconstruction_loss == "bce":
                  log_p_x_given_z = -F.binary_cross_entropy(
                      recon_x.reshape(x_rep.shape[0], -1),
                      x_rep.reshape(x_rep.shape[0], -1),
                      reduction="none",
                  ).sum(dim=-1)
  
              log_p_x.append(
                  log_p_x_given_z + log_p_z - log_q_z_given_x
              )
  
          log_p_x = torch.cat(log_p_x)
  
          log_p.append(
              (
                  torch.logsumexp(log_p_x, 0) - np.log(len(log_p_x))
              ).item()
          )
      return np.mean(log_p)
  
file_path: benchmark_VAE/src/pythae/models/pvae/pvae_model.py
imports: import warnings
  from typing import Optional
  import numpy
  import torch
  import torch.nn
  import torch.nn.functional
  from ...data.datasets import BaseDataset
  from ..base.base_utils import ModelOutput
  from ..nn import BaseDecoder, BaseEncoder
  from ..nn.default_architectures import Encoder_SVAE_MLP, Encoder_VAE_MLP
  from ..vae import VAE
  from .pvae_config import PoincareVAEConfig
  from .pvae_utils import PoincareBall, RiemannianNormal, WrappedNormal
label_desc: Use image node_id 5 for calling the PoincareVAE obj's underlying member method code with example usage: obj.get_nll(data, n_samples, batch_size) and returns: np