class_docstr: Trainer using distinct optimizers for encoder and decoder nn.
  
  Args:
      model (BaseAE): The model to train
  
      train_dataset (BaseDataset): The training dataset of type
          :class:`~pythae.data.dataset.BaseDataset`
  
      training_args (CoupledOptimizerTrainerConfig): The training arguments summarizing the main
          parameters used for training. If None, a basic training instance of
          :class:`CoupledOptimizerTrainerConfig` is used. Default: None.
  
      encoder_optimizer (~torch.optim.Optimizer): An instance of `torch.optim.Optimizer` used for
          training the encoder. If None, a :class:`~torch.optim.Adam` optimizer is used.
          Default: None.
  
      decoder_optimizer (~torch.optim.Optimizer): An instance of `torch.optim.Optimizer` used for
          training the decoder. If None, a :class:`~torch.optim.Adam` optimizer is used.
          Default: None.
docstr: Perform an evaluation step
  
  Parameters:
      epoch (int): The current epoch number
  
  Returns:
      (torch.Tensor): The evaluation loss
docstr_tok: ["Perform","an","evaluation","step","Parameters",":","epoch","(","int",")",":","The","current","epoch","number","Returns",":","(","torch.Tensor",")",":","The","evaluation","loss"]
code: def eval_step(self, epoch: int):
      """Perform an evaluation step
  
      Parameters:
          epoch (int): The current epoch number
  
      Returns:
          (torch.Tensor): The evaluation loss
      """
      self.callback_handler.on_eval_step_begin(
          training_config=self.training_config,
          eval_loader=self.eval_loader,
          epoch=epoch,
          rank=self.rank,
      )
  
      self.model.eval()
  
      epoch_encoder_loss = 0
      epoch_decoder_loss = 0
      epoch_loss = 0
  
      for inputs in self.eval_loader:
          inputs = self._set_inputs_to_device(inputs)
  
          try:
              with torch.no_grad():
                  model_output = self.model(
                      inputs,
                      epoch=epoch,
                      dataset_size=len(self.eval_loader.dataset),
                      uses_ddp=self.distributed,
                  )
  
          except RuntimeError:
              model_output = self.model(
                  inputs,
                  epoch=epoch,
                  dataset_size=len(self.eval_loader.dataset),
                  uses_ddp=self.distributed,
              )
  
          encoder_loss = model_output.encoder_loss
          decoder_loss = model_output.decoder_loss
  
          loss = encoder_loss + decoder_loss
  
          epoch_encoder_loss += encoder_loss.item()
          epoch_decoder_loss += decoder_loss.item()
          epoch_loss += loss.item()
  
          if epoch_loss != epoch_loss:
              raise ArithmeticError("NaN detected in eval loss")
  
          self.callback_handler.on_eval_step_end(
              training_config=self.training_config
          )
  
      epoch_encoder_loss /= len(self.eval_loader)
      epoch_decoder_loss /= len(self.eval_loader)
      epoch_loss /= len(self.eval_loader)
  
      return (epoch_loss, epoch_encoder_loss, epoch_decoder_loss)
  
imports: import datetime
  import logging
  import os
  from copy import deepcopy
  from typing import List, Optional
  import torch
  import torch.distributed
  import torch.optim
  import torch.optim.lr_scheduler
  from ...data.datasets import BaseDataset
  from ...models import BaseAE
  from ..base_trainer import BaseTrainer
  from ..trainer_utils import set_seed
  from ..training_callbacks import TrainingCallback
  from .coupled_optimizer_trainer_config import CoupledOptimizerTrainerConfig
label_desc: Use image node_id 10 for calling the CoupledOptimizerTrainer obj's underlying member method code with example usage: obj.eval_step(epoch) and returns: epoch_loss,  epoch_encoder_loss,  epoch_decoder_loss