# Path: difflogic/tqdm_utils.py
# def tqdm_for(total, func):
#   """wrapper of the for function with message showing on the progress bar."""
#   # Not support break cases for now.
#   with tqdm_pbar(total=total) as pbar:
#     for i in range(total):
#       message = func(i)
#       pbar.set_description(message)
#       pbar.update()
# 
# Path: difflogic/dataset/utils.py
# class RandomlyIterDataset(object):
#   """Collect data and iterate the dataset in random order."""
# 
#   def __init__(self):
#     super().__init__()
#     self.data = []
#     self.ind = 0
# 
#   def append(self, data):
#     self.data.append(data)
# 
#   @property
#   def size(self):
#     return len(self.data)
# 
#   def reset(self):
#     self.ind = 0
# 
#   def get(self):
#     """iterate the dataset with random order."""
#     # Shuffle before the iteration starts.
#     # The iteration should better be separated with collection.
#     if self.ind == 0:
#       random.shuffle(self.data)
#     ret = self.data[self.ind]
#     self.ind += 1
#     if self.ind == self.size:
#       self.ind = 0
#     return copy.deepcopy(ret)
# 
# Path: difflogic/thutils.py
# def monitor_gradrms(model):
#   """Monitor the rms of the gradients of the parameters."""
#   monitors = {}
#   for name, p in model.named_parameters():
#     if p.grad is not None:
#       monitors['gradrms/' + name] = rms(p.grad) / max(rms(p), 1e-8)
#   return monitors


import argparse
import collections
import copy
import jacinle.random as random
from ..tqdm_utils import tqdm_for
from difflogic.dataset.utils import RandomlyIterDataset
from difflogic.thutils import monitor_gradrms
from jacinle.logging import get_logger
from jacinle.utils.meter import GroupMeters
from jacinle.utils.tqdm import tqdm_pbar
from jactorch.train.env import TrainerEnv
from jactorch.utils.meta import as_cuda
      meters: a stats collector to collect information.
      mode: 'train' or 'test' or others.

    Returns:
      message: The message to be shown on tqdm progress bar.
      extra_info: An extra variable to give extra information.
    """
    raise NotImplementedError()

  def _train_step(self, feed_dict, meters):
    ret = self.step(feed_dict)
    loss, monitors, output_dict, extras = ret
    meters.update(monitor_gradrms(self.model))
    meters.update(monitors)
    meters.update(loss=loss)
    return 'Train: loss={loss:.4f}'.format(loss=loss), ret

  def _train_epoch(self, epoch_size):
    model = self.model
    meters = GroupMeters()
    self._prepare_dataset(epoch_size, mode='train')

    def train_func(index):
      model.eval()
      feed_dict = self._get_train_data(index, meters)
      model.train()
      message, _ = self._train_step(feed_dict, meters)
      return message

    # For $epoch_size times, do train_func with tqdm progress bar.

# Path: difflogic/tqdm_utils.py
# def tqdm_for(total, func):
#   """wrapper of the for function with message showing on the progress bar."""
#   # Not support break cases for now.
#   with tqdm_pbar(total=total) as pbar:
#     for i in range(total):
#       message = func(i)
#       pbar.set_description(message)
#       pbar.update()
# 
# Path: difflogic/dataset/utils.py
# class RandomlyIterDataset(object):
#   """Collect data and iterate the dataset in random order."""
# 
#   def __init__(self):
#     super().__init__()
#     self.data = []
#     self.ind = 0
# 
#   def append(self, data):
#     self.data.append(data)
# 
#   @property
#   def size(self):
#     return len(self.data)
# 
#   def reset(self):
#     self.ind = 0
# 
#   def get(self):
#     """iterate the dataset with random order."""
#     # Shuffle before the iteration starts.
#     # The iteration should better be separated with collection.
#     if self.ind == 0:
#       random.shuffle(self.data)
#     ret = self.data[self.ind]
#     self.ind += 1
#     if self.ind == self.size:
#       self.ind = 0
#     return copy.deepcopy(ret)
# 
# Path: difflogic/thutils.py
# def monitor_gradrms(model):
#   """Monitor the rms of the gradients of the parameters."""
#   monitors = {}
#   for name, p in model.named_parameters():
#     if p.grad is not None:
#       monitors['gradrms/' + name] = rms(p.grad) / max(rms(p), 1e-8)
#   return monitors


# Path: difflogic/train/train.py
import argparse
import collections
import copy
import jacinle.random as random
from ..tqdm_utils import tqdm_for
from difflogic.dataset.utils import RandomlyIterDataset
from difflogic.thutils import monitor_gradrms
from jacinle.logging import get_logger
from jacinle.utils.meter import GroupMeters
from jacinle.utils.tqdm import tqdm_pbar
from jactorch.train.env import TrainerEnv
from jactorch.utils.meta import as_cuda

      meters: a stats collector to collect information.
      mode: 'train' or 'test' or others.

    Returns:
      message: The message to be shown on tqdm progress bar.
      extra_info: An extra variable to give extra information.
    """
    raise NotImplementedError()

  def _train_step(self, feed_dict, meters):
    ret = self.step(feed_dict)
    loss, monitors, output_dict, extras = ret
    meters.update(monitor_gradrms(self.model))
    meters.update(monitors)
    meters.update(loss=loss)
    return 'Train: loss={loss:.4f}'.format(loss=loss), ret

  def _train_epoch(self, epoch_size):
    model = self.model
    meters = GroupMeters()
    self._prepare_dataset(epoch_size, mode='train')

    def train_func(index):
      model.eval()
      feed_dict = self._get_train_data(index, meters)
      model.train()
      message, _ = self._train_step(feed_dict, meters)
      return message

    # For $epoch_size times, do train_func with tqdm progress bar.

    tqdm_for(epoch_size, train_func)