class_docstr: Normalizing flows are widley used to perform generative models.
  This algorithm gives advantages over variational autoencoders (VAE) because
  of ease in sampling by applying invertible transformations
  (Frey, Gadepally, & Ramsundar, 2022).
  
  Example
  --------
  >>> import deepchem as dc
  >>> from deepchem.models.torch_models.layers import Affine
  >>> from deepchem.models.torch_models.normalizing_flows_pytorch import NormalizingFlow
  >>> import torch
  >>> from torch.distributions import MultivariateNormal
  >>> # initialize the transformation layer's parameters
  >>> dim = 2
  >>> samples = 96
  >>> transforms = [Affine(dim)]
  >>> distribution = MultivariateNormal(torch.zeros(dim), torch.eye(dim))
  >>> # initialize normalizing flow model
  >>> model = NormalizingFlow(transforms, distribution, dim)
  >>> # evaluate the log_prob when applying the transformation layers
  >>> input = distribution.sample(torch.Size((samples, dim)))
  >>> len(model.log_prob(input))
  96
  >>> # evaluates the the sampling method and its log_prob
  >>> len(model.sample(samples))
  2
docstr: Performs a sampling from the transformed distribution.
  Besides the outputs (sampling), this method returns the logarithm of
  probability to obtain the outputs at the base distribution.
  
  Parameters
  ----------
  n_samples: int
      Number of samples to select from the transformed distribution
  
  Returns
  -------
  sample: tuple
      This tuple contains a two torch.Tensor objects. The first represents
      a sampling of the learned distribution when transformations had been
      applied. The secong torc.Tensor is the computation of log probabilities
      of the transformed distribution.
      shape: ((samples, dim), (samples))
docstr_tok: ["Performs","a","sampling","from","the","transformed","distribution",".","Besides","the","outputs","(","sampling",")",",","this","method","returns","the","logarithm","of","probability","to","obtain","the","outputs","at","the","base","distribution",".","Parameters","--","--","--","--","--","n_samples",":","int","Number","of","samples","to","select","from","the","transformed","distribution","Returns","--","--","--","-","sample",":","tuple","This","tuple","contains","a","two","torch.Tensor","objects",".","The","first","represents","a","sampling","of","the","learned","distribution","when","transformations","had","been","applied",".","The","secong","torc.Tensor","is","the","computation","of","log","probabilities","of","the","transformed","distribution",".","shape",":","(","(","samples",",","dim",")",",","(","samples",")",")"]
code: def sample(self, n_samples: int) -> Tuple[torch.Tensor, torch.Tensor]:
      """Performs a sampling from the transformed distribution.
      Besides the outputs (sampling), this method returns the logarithm of
      probability to obtain the outputs at the base distribution.
  
      Parameters
      ----------
      n_samples: int
          Number of samples to select from the transformed distribution
  
      Returns
      -------
      sample: tuple
          This tuple contains a two torch.Tensor objects. The first represents
          a sampling of the learned distribution when transformations had been
          applied. The secong torc.Tensor is the computation of log probabilities
          of the transformed distribution.
          shape: ((samples, dim), (samples))
  
      """
      outputs = self.base_distribution.sample((n_samples,))
      log_prob = self.base_distribution.log_prob(outputs)
  
      for biject in self.transforms:
          outputs, log_det_jacobian = biject.forward(outputs)
          log_prob += log_det_jacobian
  
      return outputs, log_prob
  
file_path: deepchem/deepchem/models/torch_models/normalizing_flows_pytorch.py
imports: import torch
  from torch import nn
  from typing import Sequence, Tuple
label_desc: Use image node_id 3 for calling the NormalizingFlow obj's underlying member method code with example usage: obj.sample(n_samples) and returns: outputs,  log_prob