class_docstr: A task for training primal model and dual models jointly.
  It takes:
      - unlabelled source (aka source monolingual data for translation task),
      - unlabelled target (aka target monolingual data for translation task),
      - labelled (source, target) (aka parallel data for translation task),
class_docstr_tok: ["A","task","for","training","primal","model","and","dual","models","jointly",".","It","takes",":","-","unlabelled","source","(","aka","source","monolingual","data","for","translation","task",")",",","-","unlabelled","target","(","aka","target","monolingual","data","for","translation","task",")",",","-","labelled","(","source",",","target",")","(","aka","parallel","data","for","translation","task",")",","]
code: def valid_step(self, sample, model, criterion):
      agg_loss, agg_sample_size, agg_logging_output = 0.0, 0.0, {}
      if sample["primal_parallel"] is not None:
          model.eval()
          with torch.no_grad():
              loss, sample_size, logging_output = self.criterion(
                  model.models["primal"], sample["primal_parallel"]
              )
              agg_loss += loss.data.item()
              agg_sample_size += sample_size
              agg_logging_output["primal_parallel"] = logging_output
      if sample["dual_parallel"] is not None:
          model.eval()
          with torch.no_grad():
              loss, sample_size, logging_output = self.criterion(
                  model.models["dual"], sample["dual_parallel"]
              )
              agg_loss += loss.data.item()
              agg_sample_size += sample_size
              agg_logging_output["dual_parallel"] = logging_output
      return agg_loss, agg_sample_size, agg_logging_output
  
imports: from collections import OrderedDict
  import torch
  from fairseq import optim, utils
  from fairseq.criterions import CRITERION_REGISTRY
  from fairseq.data import LanguagePairDataset, RoundRobinZipDatasets
  from fairseq.tasks import LegacyFairseqTask, register_task
  from pytorch_translate.data import dictionary, utils
  from pytorch_translate.dual_learning import dual_learning_models
  from pytorch_translate.dual_learning.dual_learning_criterion import UnsupervisedCriterion
  from pytorch_translate.tasks.pytorch_translate_task import PytorchTranslateTask
  from pytorch_translate.weighted_criterions import WeightedLabelSmoothedCrossEntropyCriterion
label_desc: Use image node_id 17 for calling the DualLearningTask obj's underlying member method code with example usage: obj.valid_step(sample, model, criterion) and returns: agg_loss,  agg_sample_size,  agg_logging_output