# Path: detection/model.py
# class SingleStream(BaseModel):
#   """Model to train a single modality.
#   """
# 
#   def __init__(self, *args, **kwargs):
#     super(SingleStream, self).__init__(*args, **kwargs)
#     assert len(self.embeds) == 1
# 
#   def _backward(self, results, label):
#     logits, _ = results
#     logits = logits.view(-1, logits.size(-1))
# 
#     loss = self.criterion_cls(logits, label.view(-1))
#     loss.backward()
#     torch.nn.utils.clip_grad_norm(self.embeds[self.to_idx].parameters(), 5)
#     for optimizer in self.optimizers:
#       optimizer.step()
#       optimizer.zero_grad()
# 
#     info_loss = [('loss', loss.data[0])]
#     return info_loss
# 
# Path: detection/model.py
# class GraphDistillation(BaseModel):
#   """Model to train with graph distillation.
# 
#   xfer_to is the modality to train.
#   """
# 
#   def __init__(self, modalities, n_classes, n_frames, n_channels, input_sizes,
#                hidden_size, n_layers, dropout, hidden_size_seq, n_layers_seq,
#                dropout_seq, bg_w, lr, lr_decay_rate, to_idx, ckpt_path,
#                w_losses, w_modalities, metric, xfer_to, gd_size, gd_reg):
#     super(GraphDistillation, self).__init__(\
#                modalities, n_classes, n_frames, n_channels, input_sizes,
#                hidden_size, n_layers, dropout, hidden_size_seq, n_layers_seq, dropout_seq,
#                bg_w, lr, lr_decay_rate, to_idx, ckpt_path)
# 
#     # Index of the modality to distill
#     to_idx = self.modalities.index(xfer_to)
#     from_idx = [x for x in range(len(self.modalities)) if x != to_idx]
#     assert len(from_idx) >= 1
# 
#     # Prior
#     w_modalities = [w_modalities[i] for i in from_idx
#                    ]  # remove modality being transferred to
#     gd_prior = utils.softmax(w_modalities, 0.25)
#     # Distillation model
#     self.distillation_kernel = \
#         get_distillation_kernel(n_classes + 1, hidden_size, gd_size, to_idx, from_idx,
#                                 gd_prior, gd_reg, w_losses, metric).cuda()
# 
#     # Add optimizer to self.optimizers
#     gd_optimizer = optim.SGD(
#         self.distillation_kernel.parameters(),
#         lr=lr,
#         momentum=0.9,
#         weight_decay=5e-4)
#     self.optimizers.append(gd_optimizer)
#     self.lr_decay_rates.append(lr_decay_rate)
# 
#     self.xfer_to = xfer_to
#     self.to_idx = to_idx
#     self.from_idx = from_idx
# 
#   def _forward(self, inputs):
#     logits, reprs = super(GraphDistillation, self)._forward(inputs)
#     n_modalities, batch_size, length, _ = logits.size()
#     logits = logits.view(n_modalities, batch_size * length, -1)
#     reprs = reprs.view(n_modalities, batch_size * length, -1)
#     # Get edge weights of the graph
#     graph = self.distillation_kernel(logits, reprs)
#     return logits, reprs, graph
# 
#   def _backward(self, results, label):
#     logits, reprs, graph = results  # graph: size (len(from_idx) x batch_size)
#     label = label.view(-1)
#     info_loss = []
# 
#     # Classification loss
#     loss_cls = self.criterion_cls(logits[self.to_idx], label)
#     # Graph distillation loss
#     loss_reg, loss_logit, loss_repr = \
#         self.distillation_kernel.distillation_loss(logits, reprs, graph)
# 
#     loss = loss_cls + loss_reg + loss_logit + loss_repr
#     loss.backward()
#     torch.nn.utils.clip_grad_norm(self.embeds[self.to_idx].parameters(), 5)
#     for optimizer in self.optimizers:
#       optimizer.step()
#       optimizer.zero_grad()
# 
#     info_loss = [('loss_cls', loss_cls.data[0]), ('loss_reg', loss_reg.data[0]),
#                  ('loss_logit', loss_logit.data[0]), ('loss_repr',
#                                                       loss_repr.data[0])]
#     return info_loss


from .model import SingleStream
from .model import GraphDistillation

"""Get detection model."""


ALL_MODALITIES = ['rgb', 'oflow', 'depth', 'jjd', 'jjv', 'jld']


def get_model(opt):
  if opt.dset == 'pku-mmd':
    n_classes = 51
    all_input_sizes = [-1, -1, -1, 276, 828, 836]
    all_n_channels = [3, 2, 1, -1, -1, -1]
  else:
    raise NotImplementedError

  n_channels = [all_n_channels[ALL_MODALITIES.index(m)] for m in opt.modalities]
  input_sizes = [
      all_input_sizes[ALL_MODALITIES.index(m)] for m in opt.modalities
  ]

  if len(opt.modalities) == 1:
    # Single stream
    index = 0
    model = SingleStream(opt.modalities, n_classes, opt.n_frames, n_channels,
                         input_sizes, opt.hidden_size, opt.n_layers,
                         opt.dropout, opt.hidden_size_seq, opt.n_layers_seq,
                         opt.dropout_seq, opt.bg_w, opt.lr, opt.lr_decay_rate,
                         index, opt.ckpt_path)
  else:
    index = opt.modalities.index(opt.xfer_to)

# Path: detection/model.py
# class SingleStream(BaseModel):
#   """Model to train a single modality.
#   """
# 
#   def __init__(self, *args, **kwargs):
#     super(SingleStream, self).__init__(*args, **kwargs)
#     assert len(self.embeds) == 1
# 
#   def _backward(self, results, label):
#     logits, _ = results
#     logits = logits.view(-1, logits.size(-1))
# 
#     loss = self.criterion_cls(logits, label.view(-1))
#     loss.backward()
#     torch.nn.utils.clip_grad_norm(self.embeds[self.to_idx].parameters(), 5)
#     for optimizer in self.optimizers:
#       optimizer.step()
#       optimizer.zero_grad()
# 
#     info_loss = [('loss', loss.data[0])]
#     return info_loss
# 
# Path: detection/model.py
# class GraphDistillation(BaseModel):
#   """Model to train with graph distillation.
# 
#   xfer_to is the modality to train.
#   """
# 
#   def __init__(self, modalities, n_classes, n_frames, n_channels, input_sizes,
#                hidden_size, n_layers, dropout, hidden_size_seq, n_layers_seq,
#                dropout_seq, bg_w, lr, lr_decay_rate, to_idx, ckpt_path,
#                w_losses, w_modalities, metric, xfer_to, gd_size, gd_reg):
#     super(GraphDistillation, self).__init__(\
#                modalities, n_classes, n_frames, n_channels, input_sizes,
#                hidden_size, n_layers, dropout, hidden_size_seq, n_layers_seq, dropout_seq,
#                bg_w, lr, lr_decay_rate, to_idx, ckpt_path)
# 
#     # Index of the modality to distill
#     to_idx = self.modalities.index(xfer_to)
#     from_idx = [x for x in range(len(self.modalities)) if x != to_idx]
#     assert len(from_idx) >= 1
# 
#     # Prior
#     w_modalities = [w_modalities[i] for i in from_idx
#                    ]  # remove modality being transferred to
#     gd_prior = utils.softmax(w_modalities, 0.25)
#     # Distillation model
#     self.distillation_kernel = \
#         get_distillation_kernel(n_classes + 1, hidden_size, gd_size, to_idx, from_idx,
#                                 gd_prior, gd_reg, w_losses, metric).cuda()
# 
#     # Add optimizer to self.optimizers
#     gd_optimizer = optim.SGD(
#         self.distillation_kernel.parameters(),
#         lr=lr,
#         momentum=0.9,
#         weight_decay=5e-4)
#     self.optimizers.append(gd_optimizer)
#     self.lr_decay_rates.append(lr_decay_rate)
# 
#     self.xfer_to = xfer_to
#     self.to_idx = to_idx
#     self.from_idx = from_idx
# 
#   def _forward(self, inputs):
#     logits, reprs = super(GraphDistillation, self)._forward(inputs)
#     n_modalities, batch_size, length, _ = logits.size()
#     logits = logits.view(n_modalities, batch_size * length, -1)
#     reprs = reprs.view(n_modalities, batch_size * length, -1)
#     # Get edge weights of the graph
#     graph = self.distillation_kernel(logits, reprs)
#     return logits, reprs, graph
# 
#   def _backward(self, results, label):
#     logits, reprs, graph = results  # graph: size (len(from_idx) x batch_size)
#     label = label.view(-1)
#     info_loss = []
# 
#     # Classification loss
#     loss_cls = self.criterion_cls(logits[self.to_idx], label)
#     # Graph distillation loss
#     loss_reg, loss_logit, loss_repr = \
#         self.distillation_kernel.distillation_loss(logits, reprs, graph)
# 
#     loss = loss_cls + loss_reg + loss_logit + loss_repr
#     loss.backward()
#     torch.nn.utils.clip_grad_norm(self.embeds[self.to_idx].parameters(), 5)
#     for optimizer in self.optimizers:
#       optimizer.step()
#       optimizer.zero_grad()
# 
#     info_loss = [('loss_cls', loss_cls.data[0]), ('loss_reg', loss_reg.data[0]),
#                  ('loss_logit', loss_logit.data[0]), ('loss_repr',
#                                                       loss_repr.data[0])]
#     return info_loss


# Path: detection/get_model.py
from .model import SingleStream
from .model import GraphDistillation


"""Get detection model."""


ALL_MODALITIES = ['rgb', 'oflow', 'depth', 'jjd', 'jjv', 'jld']


def get_model(opt):
  if opt.dset == 'pku-mmd':
    n_classes = 51
    all_input_sizes = [-1, -1, -1, 276, 828, 836]
    all_n_channels = [3, 2, 1, -1, -1, -1]
  else:
    raise NotImplementedError

  n_channels = [all_n_channels[ALL_MODALITIES.index(m)] for m in opt.modalities]
  input_sizes = [
      all_input_sizes[ALL_MODALITIES.index(m)] for m in opt.modalities
  ]

  if len(opt.modalities) == 1:
    # Single stream
    index = 0
    model = SingleStream(opt.modalities, n_classes, opt.n_frames, n_channels,
                         input_sizes, opt.hidden_size, opt.n_layers,
                         opt.dropout, opt.hidden_size_seq, opt.n_layers_seq,
                         opt.dropout_seq, opt.bg_w, opt.lr, opt.lr_decay_rate,
                         index, opt.ckpt_path)
  else:
    index = opt.modalities.index(opt.xfer_to)

    model = GraphDistillation(