class_docstr: A stack of FCLayers.
  
  The specification of each FCLayer is specified by the `layers` dictionary parameter, whose keys correspond with an
  FCLayer's constructor arguments, i.e.
  
  [
      {"input_size": 2, "output_size": 4},
      {"output_size": 4, "use_bias": False},
  ]
  
  `default_*` parameters dictate default values to use for each FCLayer, if not specified by `layers`. If `layers` is
  `None`, then a stack of size `num_layers` of `FCLayer`s configured with all of the `default_*` parameters is used.
  
  If `layers` is None and `num_layers` is 0, then there are no fully connected layers and this module serves as a
  trivial passthrough.
class_docstr_tok: ["A","stack","of","FCLayers",".","The","specification","of","each","FCLayer","is","specified","by","the","`","layers","`","dictionary","parameter",",","whose","keys","correspond","with","an","FCLayer","'s","constructor","arguments",",","i.e",".","[","{","``","input_size","''",":","2",",","``","output_size","''",":","4","}",",","{","``","output_size","''",":","4",",","``","use_bias","''",":","False","}",",","]","`","default_","*","`","parameters","dictate","default","values","to","use","for","each","FCLayer",",","if","not","specified","by","`","layers","`",".","If","`","layers","`","is","`","None","`",",","then","a","stack","of","size","`","num_layers","`","of","`","FCLayer","`","s","configured","with","all","of","the","`","default_","*","`","parameters","is","used",".","If","`","layers","`","is","None","and","`","num_layers","`","is","0",",","then","there","are","no","fully","connected","layers","and","this","module","serves","as","a","trivial","passthrough","."]
code: def __init__(
      self,
      first_layer_input_size: int,
      layers: Optional[List[Dict]] = None,
      num_layers: int = 1,
      default_input_rank: int = 2,
      default_output_size: int = 256,
      default_use_bias: bool = True,
      default_weights_initializer: str = "xavier_uniform",
      default_bias_initializer: str = "zeros",
      default_norm: Optional[str] = None,
      default_norm_params: Optional[Dict] = None,
      default_activation: str = "relu",
      default_dropout: float = 0,
      residual: bool = False,
      **kwargs,
  ):
      super().__init__()
      self.input_size = first_layer_input_size
  
      self.norm_layer = None
      if default_norm is not None:
          norm_params = default_norm_params or {}
          self.norm_layer = create_norm_layer(
              default_norm,
              default_input_rank,
              self.input_size,
              **norm_params,
          )
  
      self.dropout = None
      if default_dropout > 0:
          self.dropout = torch.nn.Dropout(default_dropout)
  
      if layers is None:
          self.layers = []
          for i in range(num_layers):
              self.layers.append({})
      else:
          # deep copy the layer definitions so that we don't modify the original
          self.layers = deepcopy(layers)
  
      if len(self.layers) > 0 and "input_size" not in self.layers[0]:
          self.layers[0]["input_size"] = first_layer_input_size
      for i, layer in enumerate(self.layers):
          if i != 0:
              layer["input_size"] = self.layers[i - 1]["output_size"]
          if "input_rank" not in layer:
              layer["input_rank"] = default_input_rank
          if "output_size" not in layer:
              layer["output_size"] = default_output_size
          if "use_bias" not in layer:
              layer["use_bias"] = default_use_bias
          if "weights_initializer" not in layer:
              layer["weights_initializer"] = default_weights_initializer
          if "bias_initializer" not in layer:
              layer["bias_initializer"] = default_bias_initializer
          if "norm" not in layer:
              layer["norm"] = default_norm
          if "norm_params" not in layer:
              layer["norm_params"] = default_norm_params
          if "activation" not in layer:
              layer["activation"] = default_activation
          if "dropout" not in layer:
              layer["dropout"] = default_dropout
  
      self.stack = ModuleList()
  
      for i, layer in enumerate(self.layers):
          self.stack.append(
              FCLayer(
                  input_size=layer["input_size"],
                  input_rank=layer["input_rank"],
                  output_size=layer["output_size"],
                  use_bias=layer["use_bias"],
                  weights_initializer=layer["weights_initializer"],
                  bias_initializer=layer["bias_initializer"],
                  norm=layer["norm"],
                  norm_params=layer["norm_params"],
                  activation=layer["activation"],
                  dropout=layer["dropout"],
              )
          )
      self.residual = residual
  
imports: import logging
  from copy import deepcopy
  from typing import Dict, List, Optional
  import torch
  from torch.nn import Dropout, Linear, ModuleList
  from ludwig.modules.normalization_modules import create_norm_layer
  from ludwig.utils.torch_utils import activations, initializer_registry, LudwigModule
label_desc: Use image node_id 1 to create a new FCStack object from inherited base classes: LudwigModule with example: obj = FCStack(first_layer_input_size, layers, num_layers, default_input_rank, default_output_size, default_use_bias, default_weights_initializer, default_bias_initializer, default_norm, default_norm_params, default_activation, default_dropout, residual)