class_docstr: Wrapper for nn.LSTM
  
  Differences include:
  * weight initialization
  * the bidirectional option makes the first layer bidirectional only
  (and in that case the hidden dim is divided by 2)
class_docstr_tok: ["Wrapper","for","nn.LSTM","Differences","include",":","*","weight","initialization","*","the","bidirectional","option","makes","the","first","layer","bidirectional","only","(","and","in","that","case","the","hidden","dim","is","divided","by","2",")"]
code: def forward(
      self,
      embeddings: torch.Tensor,
      lengths: torch.Tensor,
      enforce_sorted: bool = True,
  ):
      # enforce_sorted is set to True by default to force input lengths
      # are sorted in a descending order when pack padded sequence.
      bsz = embeddings.size()[1]
  
      # Generate packed seq to deal with varying source seq length
      # packed_input is of type PackedSequence, which consists of:
      # element [0]: a tensor, the packed data, and
      # element [1]: a list of integers, the batch size for each step
      packed_input = pack_padded_sequence(
          embeddings, lengths, enforce_sorted=enforce_sorted
      )
  
      final_hiddens, final_cells = [], []
      for i, rnn_layer in enumerate(self.layers):
          if self.bidirectional and i == 0:
              h0 = embeddings.new_full(
                  (2, bsz, self.hidden_dim // 2), 0
              )
              c0 = embeddings.new_full(
                  (2, bsz, self.hidden_dim // 2), 0
              )
          else:
              h0 = embeddings.new_full((1, bsz, self.hidden_dim), 0)
              c0 = embeddings.new_full((1, bsz, self.hidden_dim), 0)
  
          # apply LSTM along entire sequence
          current_output, (h_last, c_last) = rnn_layer(
              packed_input, (h0, c0)
          )
  
          # final state shapes: (bsz, hidden_dim)
          if self.bidirectional and i == 0:
              # concatenate last states for forward and backward LSTM
              h_last = torch.cat(
                  (h_last[0, :, :], h_last[1, :, :]), dim=1
              )
              c_last = torch.cat(
                  (c_last[0, :, :], c_last[1, :, :]), dim=1
              )
          else:
              h_last = h_last.squeeze(dim=0)
              c_last = c_last.squeeze(dim=0)
  
          final_hiddens.append(h_last)
          final_cells.append(c_last)
  
          packed_input = current_output
  
      # Reshape to [num_layer, batch_size, hidden_dim]
      final_hidden_size_list: List[int] = final_hiddens[0].size()
      final_hidden_size: Tuple[int, int] = (
          final_hidden_size_list[0],
          final_hidden_size_list[1],
      )
      final_hiddens = torch.cat(final_hiddens, dim=0).view(
          self.num_layers, *final_hidden_size
      )
      final_cell_size_list: List[int] = final_cells[0].size()
      final_cell_size: Tuple[int, int] = (
          final_cell_size_list[0],
          final_cell_size_list[1],
      )
      final_cells = torch.cat(final_cells, dim=0).view(
          self.num_layers, *final_cell_size
      )
  
      #  [max_seqlen, batch_size, hidden_dim]
      unpacked_output, _ = pad_packed_sequence(packed_input)
  
      return (unpacked_output, final_hiddens, final_cells)
  
file_path: pytext/pytext/models/seq_models/rnn_encoder.py
imports: from typing import Dict, List, Tuple
  import torch
  import torch.nn.functional
  from pytext.config import ConfigBase
  from pytext.utils.usage import log_class_usage
  from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
  from .base import PyTextSeq2SeqModule
label_desc: Use image node_id 3 for calling the BiLSTM obj's underlying member method code with example usage: obj.forward(embeddings, lengths, enforce_sorted) and returns: unpacked_output,  final_hiddens,  final_cells