class_docstr: ## Encoder module
  
  This consists of a bidirectional LSTM
class_docstr_tok: ["#","#","Encoder","module","This","consists","of","a","bidirectional","LSTM"]
code: def forward(self, inputs: torch.Tensor, state=None):
      # The hidden state of the bidirectional LSTM is the concatenation of the
      # output of the last token in the forward direction and
      # first token in the reverse direction, which is what we want.
      # $$h_{\rightarrow} = encode_{\rightarrow}(S),
      # h_{\leftarrow} = encode←_{\leftarrow}(S_{reverse}),
      # h = [h_{\rightarrow}; h_{\leftarrow}]$$
      _, (hidden, cell) = self.lstm(inputs.float(), state)
      # The state has shape `[2, batch_size, hidden_size]`,
      # where the first dimension is the direction.
      # We rearrange it to get $h = [h_{\rightarrow}; h_{\leftarrow}]$
      hidden = einops.rearrange(hidden, "fb b h -> b (fb h)")
  
      # $\mu$
      mu = self.mu_head(hidden)
      # $\hat{\sigma}$
      sigma_hat = self.sigma_head(hidden)
      # $\sigma = \exp(\frac{\hat{\sigma}}{2})$
      sigma = torch.exp(sigma_hat / 2.0)
  
      # Sample $z = \mu + \sigma \cdot \mathcal{N}(0, I)$
      z = mu + sigma * torch.normal(
          mu.new_zeros(mu.shape), mu.new_ones(mu.shape)
      )
  
      #
      return z, mu, sigma_hat
  
imports: import math
  from typing import Optional, Tuple, Any
  import numpy
  import torch
  import torch.nn
  from matplotlib import pyplot
  from torch import optim
  from torch.utils.data import Dataset, DataLoader
  import einops
  from labml import lab, experiment, tracker, monit
  from labml_helpers.device import DeviceConfigs
  from labml_helpers.module import Module
  from labml_helpers.optimizer import OptimizerConfigs
  from labml_helpers.train_valid import TrainValidConfigs, hook_model_outputs, BatchIndex
label_desc: Use image node_id 2 for calling the EncoderRNN obj's underlying member method code with example usage: obj.forward(inputs, state) and returns: z,  mu,  sigma_hat