class_docstr: Builder class for Generative Adversarial Networks.
  
  A Generative Adversarial Network (GAN) is a type of generative model. It
  consists of two parts called the "generator" and the "discriminator". The
  generator takes random noise as input and transforms it into an output that
  (hopefully) resembles the training data. The discriminator takes a set of
  samples as input and tries to distinguish the real training samples from the
  ones created by the generator. Both of them are trained together. The
  discriminator tries to get better and better at telling real from false data,
  while the generator tries to get better and better at fooling the discriminator.
  
  Examples
  --------
  Importing necessary modules
  
  >>> import deepchem as dc
  >>> from deepchem.models.torch_models.gan import GAN
  >>> import torch
  >>> import torch.nn as nn
  >>> import torch.nn.functional as F
  
  Creating a Generator
  
  >>> class Generator(nn.Module):
  ...     def __init__(self, noise_input_shape, conditional_input_shape):
  ...         super(Generator, self).__init__()
  ...         self.noise_input_shape = noise_input_shape
  ...         self.conditional_input_shape = conditional_input_shape
  ...         self.noise_dim = noise_input_shape[1:]
  ...         self.conditional_dim = conditional_input_shape[1:]
  ...         input_dim = sum(self.noise_dim) + sum(self.conditional_dim)
  ...         self.output = nn.Linear(input_dim, 1)
  ...     def forward(self, input):
  ...         noise_input, conditional_input = input
  ...         inputs = torch.cat((noise_input, conditional_input), dim=1)
  ...         output = self.output(inputs)
  ...         return output
  
  Creating a Discriminator
  
  >>> class Discriminator(nn.Module):
  ...     def __init__(self, data_input_shape, conditional_input_shape):
  ...         super(Discriminator, self).__init__()
  ...         self.data_input_shape = data_input_shape
  ...         self.conditional_input_shape = conditional_input_shape
  ...         # Extracting the actual data dimension
  ...         data_dim = data_input_shape[1:]
  ...         # Extracting the actual conditional dimension
  ...         conditional_dim = conditional_input_shape[1:]
  ...         input_dim = sum(data_dim) + sum(conditional_dim)
  ...         # Define the dense layers
  ...         self.dense1 = nn.Linear(input_dim, 10)
  ...         self.dense2 = nn.Linear(10, 1)
  ...     def forward(self, input):
  ...         data_input, conditional_input = input
  ...         # Concatenate data_input and conditional_input along the second dimension
  ...         discrim_in = torch.cat((data_input, conditional_input), dim=1)
  ...         # Pass the concatenated input through the dense layers
  ...         x = F.relu(self.dense1(discrim_in))
  ...         output = torch.sigmoid(self.dense2(x))
  ...         return output
  
  Defining an Example GAN class
  
  >>> class ExampleGAN(dc.models.torch_models.GAN):
  ...    def get_noise_input_shape(self):
  ...        return (16,2,)
  ...    def get_data_input_shapes(self):
  ...        return [(16,1,)]
  ...    def get_conditional_input_shapes(self):
  ...        return [(16,1,)]
  ...    def create_generator(self):
  ...        noise_dim = self.get_noise_input_shape()
  ...        conditional_dim = self.get_conditional_input_shapes()[0]
  ...        return nn.Sequential(Generator(noise_dim, conditional_dim))
  ...    def create_discriminator(self):
  ...        data_input_shape = self.get_data_input_shapes()[0]
  ...        conditional_input_shape = self.get_conditional_input_shapes()[0]
  ...        return nn.Sequential(
  ...            Discriminator(data_input_shape, conditional_input_shape))
  
  Defining the GAN
  
  >>> batch_size = 16
  >>> noise_shape = (batch_size, 2,)
  >>> data_shape = [(batch_size, 1,)]
  >>> conditional_shape = [(batch_size, 1,)]
  >>> def create_generator(noise_dim, conditional_dim):
  ...     noise_dim = noise_dim
  ...     conditional_dim = conditional_dim[0]
  ...     return nn.Sequential(Generator(noise_dim, conditional_dim))
  >>> def create_discriminator(data_input_shape, conditional_input_shape):
  ...     data_input_shape = data_input_shape[0]
  ...     conditional_input_shape = conditional_input_shape[0]
  ...     return nn.Sequential(
  ...         Discriminator(data_input_shape, conditional_input_shape))
  >>> gan = ExampleGAN(noise_shape, data_shape, conditional_shape,
  ...                  create_generator(noise_shape, conditional_shape),
  ...                  create_discriminator(data_shape, conditional_shape))
  >>> noise = torch.rand(*gan.noise_input_shape)
  >>> real_data = torch.rand(*gan.data_input_shape[0])
  >>> conditional = torch.rand(*gan.conditional_input_shape[0])
  >>> gen_loss, disc_loss = gan([noise, real_data, conditional])
  
  References
  ----------
  .. [1] Goodfellow, Ian, et al. "Generative adversarial nets." Advances in
       neural information processing systems. 2014.
  
  .. [2] Arora et al., “Generalization and Equilibrium in Generative
       Adversarial Nets (GANs)” (https://arxiv.org/abs/1703.00573)
  
  Notes
  -----
  This class is a subclass of TorchModel.  It accepts all the keyword arguments
  from TorchModel.
class_docstr_tok: ["Builder","class","for","Generative","Adversarial","Networks",".","A","Generative","Adversarial","Network","(","GAN",")","is","a","type","of","generative","model",".","It","consists","of","two","parts","called","the","``","generator","''","and","the","``","discriminator","''",".","The","generator","takes","random","noise","as","input","and","transforms","it","into","an","output","that","(","hopefully",")","resembles","the","training","data",".","The","discriminator","takes","a","set","of","samples","as","input","and","tries","to","distinguish","the","real","training","samples","from","the","ones","created","by","the","generator",".","Both","of","them","are","trained","together",".","The","discriminator","tries","to","get","better","and","better","at","telling","real","from","false","data",",","while","the","generator","tries","to","get","better","and","better","at","fooling","the","discriminator",".","Examples","--","--","--","--","Importing","necessary","modules",">",">",">","import","deepchem","as","dc",">",">",">","from","deepchem.models.torch_models.gan","import","GAN",">",">",">","import","torch",">",">",">","import","torch.nn","as","nn",">",">",">","import","torch.nn.functional","as","F","Creating","a","Generator",">",">",">","class","Generator","(","nn.Module",")",":","...","def","__init__","(","self",",","noise_input_shape",",","conditional_input_shape",")",":","...","super","(","Generator",",","self",")",".__init__","(",")","...","self.noise_input_shape","=","noise_input_shape","...","self.conditional_input_shape","=","conditional_input_shape","...","self.noise_dim","=","noise_input_shape","[","1",":","]","...","self.conditional_dim","=","conditional_input_shape","[","1",":","]","...","input_dim","=","sum","(","self.noise_dim",")","+","sum","(","self.conditional_dim",")","...","self.output","=","nn.Linear","(","input_dim",",","1",")","...","def","forward","(","self",",","input",")",":","...","noise_input",",","conditional_input","=","input","...","inputs","=","torch.cat","(","(","noise_input",",","conditional_input",")",",","dim=1",")","...","output","=","self.output","(","inputs",")","...","return","output","Creating","a","Discriminator",">",">",">","class","Discriminator","(","nn.Module",")",":","...","def","__init__","(","self",",","data_input_shape",",","conditional_input_shape",")",":","...","super","(","Discriminator",",","self",")",".__init__","(",")","...","self.data_input_shape","=","data_input_shape","...","self.conditional_input_shape","=","conditional_input_shape","...","#","Extracting","the","actual","data","dimension","...","data_dim","=","data_input_shape","[","1",":","]","...","#","Extracting","the","actual","conditional","dimension","...","conditional_dim","=","conditional_input_shape","[","1",":","]","...","input_dim","=","sum","(","data_dim",")","+","sum","(","conditional_dim",")","...","#","Define","the","dense","layers","...","self.dense1","=","nn.Linear","(","input_dim",",","10",")","...","self.dense2","=","nn.Linear","(","10",",","1",")","...","def","forward","(","self",",","input",")",":","...","data_input",",","conditional_input","=","input","...","#","Concatenate","data_input","and","conditional_input","along","the","second","dimension","...","discrim_in","=","torch.cat","(","(","data_input",",","conditional_input",")",",","dim=1",")","...","#","Pass","the","concatenated","input","through","the","dense","layers","...","x","=","F.relu","(","self.dense1","(","discrim_in",")",")","...","output","=","torch.sigmoid","(","self.dense2","(","x",")",")","...","return","output","Defining","an","Example","GAN","class",">",">",">","class","ExampleGAN","(","dc.models.torch_models.GAN",")",":","...","def","get_noise_input_shape","(","self",")",":","...","return","(","16,2",",",")","...","def","get_data_input_shapes","(","self",")",":","...","return","[","(","16,1",",",")","]","...","def","get_conditional_input_shapes","(","self",")",":","...","return","[","(","16,1",",",")","]","...","def","create_generator","(","self",")",":","...","noise_dim","=","self.get_noise_input_shape","(",")","...","conditional_dim","=","self.get_conditional_input_shapes","(",")","[","0","]","...","return","nn.Sequential","(","Generator","(","noise_dim",",","conditional_dim",")",")","...","def","create_discriminator","(","self",")",":","...","data_input_shape","=","self.get_data_input_shapes","(",")","[","0","]","...","conditional_input_shape","=","self.get_conditional_input_shapes","(",")","[","0","]","...","return","nn.Sequential","(","...","Discriminator","(","data_input_shape",",","conditional_input_shape",")",")","Defining","the","GAN",">",">",">","batch_size","=","16",">",">",">","noise_shape","=","(","batch_size",",","2",",",")",">",">",">","data_shape","=","[","(","batch_size",",","1",",",")","]",">",">",">","conditional_shape","=","[","(","batch_size",",","1",",",")","]",">",">",">","def","create_generator","(","noise_dim",",","conditional_dim",")",":","...","noise_dim","=","noise_dim","...","conditional_dim","=","conditional_dim","[","0","]","...","return","nn.Sequential","(","Generator","(","noise_dim",",","conditional_dim",")",")",">",">",">","def","create_discriminator","(","data_input_shape",",","conditional_input_shape",")",":","...","data_input_shape","=","data_input_shape","[","0","]","...","conditional_input_shape","=","conditional_input_shape","[","0","]","...","return","nn.Sequential","(","...","Discriminator","(","data_input_shape",",","conditional_input_shape",")",")",">",">",">","gan","=","ExampleGAN","(","noise_shape",",","data_shape",",","conditional_shape",",","...","create_generator","(","noise_shape",",","conditional_shape",")",",","...","create_discriminator","(","data_shape",",","conditional_shape",")",")",">",">",">","noise","=","torch.rand","(","*","gan.noise_input_shape",")",">",">",">","real_data","=","torch.rand","(","*","gan.data_input_shape","[","0","]",")",">",">",">","conditional","=","torch.rand","(","*","gan.conditional_input_shape","[","0","]",")",">",">",">","gen_loss",",","disc_loss","=","gan","(","[","noise",",","real_data",",","conditional","]",")","References","--","--","--","--","--","..","[","1","]","Goodfellow",",","Ian",",","et","al",".","``","Generative","adversarial","nets",".","''","Advances","in","neural","information","processing","systems",".","2014",".","..","[","2","]","Arora","et","al.",",","\u201c","Generalization","and","Equilibrium","in","Generative","Adversarial","Nets","(","GANs",")","\u201d","(","https",":","\/\/arxiv.org\/abs\/1703.00573",")","Notes","--","--","-","This","class","is","a","subclass","of","TorchModel",".","It","accepts","all","the","keyword","arguments","from","TorchModel","."]
docstr: Compute the output of the GAN.
  
  Parameters
  ----------
  inputs: list of Tensor
      the inputs to the GAN. The first element must be a batch of noise,
      followed by data inputs and any conditional inputs.
  
  Returns
  -------
  total_gen_loss: Tensor
      the total loss for the generator
  total_discrim_loss: Tensor
      the total loss for the discriminator
docstr_tok: ["Compute","the","output","of","the","GAN",".","Parameters","--","--","--","--","--","inputs",":","list","of","Tensor","the","inputs","to","the","GAN",".","The","first","element","must","be","a","batch","of","noise",",","followed","by","data","inputs","and","any","conditional","inputs",".","Returns","--","--","--","-","total_gen_loss",":","Tensor","the","total","loss","for","the","generator","total_discrim_loss",":","Tensor","the","total","loss","for","the","discriminator"]
code: def forward(self, inputs) -> Tuple[torch.Tensor, torch.Tensor]:
      """Compute the output of the GAN.
  
      Parameters
      ----------
      inputs: list of Tensor
          the inputs to the GAN. The first element must be a batch of noise,
          followed by data inputs and any conditional inputs.
  
      Returns
      -------
      total_gen_loss: Tensor
          the total loss for the generator
      total_discrim_loss: Tensor
          the total loss for the discriminator
      """
  
      n_generators = self.n_generators
      n_discriminators = self.n_discriminators
      noise_input, data_input_layers, conditional_input_layers = (
          inputs[0],
          inputs[1],
          inputs[2],
      )
  
      self.noise_input.data = noise_input
      self.conditional_input_layers = [conditional_input_layers]
      self.data_input_layers = [data_input_layers]
  
      # Forward pass through generators
      generator_outputs = [
          gen(
              _list_or_tensor(
                  [[noise_input] + self.conditional_input_layers]
              )
          )
          for gen in self.generators
      ]
  
      # Forward pass through discriminators
      discrim_train_outputs = [
          self._call_discriminator(disc, self.data_input_layers, True)
          for disc in self.discriminators
      ]
  
      discrim_gen_outputs = [
          self._call_discriminator(disc, [gen_output], False)
          for disc in self.discriminators
          for gen_output in generator_outputs
      ]
  
      # Compute loss functions
      gen_losses = [
          self.create_generator_loss(d) for d in discrim_gen_outputs
      ]
      discrim_losses = [
          self.create_discriminator_loss(
              discrim_train_outputs[i],
              discrim_gen_outputs[i * self.n_generators + j],
          )
          for i in range(self.n_discriminators)
          for j in range(self.n_generators)
      ]
  
      # Compute the weighted errors
      if n_generators == 1 and n_discriminators == 1:
          total_gen_loss = gen_losses[0]
          total_discrim_loss = discrim_losses[0]
      else:
          # Create learnable weights for the generators and discriminators.
  
          gen_alpha = nn.Parameter(torch.ones(1, n_generators))
          gen_weights = nn.Parameter(torch.softmax(gen_alpha, dim=1))
  
          discrim_alpha = nn.Parameter(torch.ones(1, n_discriminators))
          discrim_weights = nn.Parameter(
              torch.softmax(discrim_alpha, dim=1)
          )
  
          # Compute the weighted errors
  
          discrim_weights_n = discrim_weights.view(
              -1, self.n_discriminators, 1
          )
          gen_weights_n = gen_weights.view(-1, 1, self.n_generators)
  
          weight_products = torch.mul(discrim_weights_n, gen_weights_n)
          weight_products = weight_products.view(
              -1, self.n_generators * self.n_discriminators
          )
  
          stacked_gen_loss = torch.stack(gen_losses, dim=0)
          stacked_discrim_loss = torch.stack(discrim_losses, dim=0)
  
          total_gen_loss = torch.sum(stacked_gen_loss * weight_products)
          total_discrim_loss = torch.sum(
              stacked_discrim_loss * weight_products
          )
  
          self.gen_variables += [gen_alpha]
          self.discrim_variables += [gen_alpha]
          self.discrim_variables += [discrim_alpha]
  
          # Add an entropy term to the loss.
  
          entropy = -(
              torch.sum(torch.log(gen_weights)) / n_generators
              + torch.sum(torch.log(discrim_weights)) / n_discriminators
          )
          total_discrim_loss = total_discrim_loss + entropy
  
      return total_gen_loss, total_discrim_loss
  
imports: import numpy
  import time
  from typing import Callable, Any, List, Tuple, Union
  from deepchem.models.torch_models.torch_model import TorchModel
label_desc: Use image node_id 2 for calling the GAN obj's underlying member method code with example usage: obj.forward(inputs) and returns: total_gen_loss,  total_discrim_loss