//  [python]
def get_feedback_loss(args, model_pos, criterion, summary, writer,
                      inputs_2d, inputs_3d, outputs_2d_ba, outputs_3d_ba, outputs_2d_rt, outputs_3d_rt):
    def get_posenet_loss(input_pose_2d, target_pose_3d):
        predict_pose_3d = model_pos(input_pose_2d.view(num_poses, -1)).view(num_poses, -1, 3)
        target_pose_3d_rooted = target_pose_3d[:, :, :] - target_pose_3d[:, :1, :]  # ignore the 0 joint
        posenet_loss = torch.norm(predict_pose_3d - target_pose_3d_rooted, dim=-1)  # return b x j loss
        weights = torch.Tensor([1, 5, 2, 1, 5, 2, 1, 1, 1, 1, 5, 2, 1, 5, 2, 1]).to(target_pose_3d.device).unsqueeze(0)
        posenet_loss = posenet_loss * weights

        posenet_loss = torch.cat([
            torch.mean(posenet_loss[:, [1, 4, 10, 13]], dim=-1, keepdim=True),
            torch.mean(posenet_loss[:, [2, 5, 9, 11, 14]], dim=-1, keepdim=True),
            torch.mean(posenet_loss[:, [3, 6, 12, 15]], dim=-1, keepdim=True)
        ], dim=-1)
        return posenet_loss

    def update_hardratio(start, end, current_epoch, total_epoch):
        return start + (end - start) * current_epoch / total_epoch

    def fix_hard_ratio_loss(expected_hard_ratio, harder, easier):  # similar to MSE
        return torch.abs(1 - torch.exp(harder - expected_hard_ratio * easier))

    def fix_hardratio(target_std, taget_mean, harder, easier, gloss_factordiv, gloss_factorfeedback, tag=''):
        harder_value = harder / easier

        hard_std = torch.std(harder_value)
        hard_mean = torch.mean(harder_value)

        hard_div_loss = torch.mean((hard_std - target_std) ** 2)
        hard_mean_loss = diff_range_loss(harder_value, taget_mean, target_std)

        writer.add_scalar('train_G_iter_posenet_feedback/{}_hard_std'.format(tag), hard_std.mean().item(),
                          summary.train_iter_num)
        writer.add_scalar('train_G_iter_posenet_feedback/{}_hard_mean'.format(tag), hard_mean.mean().item(),
                          summary.train_iter_num)
        writer.add_scalar('train_G_iter_posenet_feedback/{}_hard_sample'.format(tag), harder_value[0].mean().item(),
                          summary.train_iter_num)
        writer.add_scalar('train_G_iter_posenet_feedback/{}_hard_mean_loss'.format(tag), hard_mean_loss.item(),
                          summary.train_iter_num)
        writer.add_scalar('train_G_iter_posenet_feedback/{}_hard_std_loss'.format(tag), hard_div_loss.item(),
                          summary.train_iter_num)
        return hard_div_loss * gloss_factordiv + hard_mean_loss * gloss_factorfeedback

    # posenet loss: to generate harder case.
    # the flow: original pose --> pose BA --> pose RT
    ###################################################
    device = torch.device("cuda")
    num_poses = inputs_2d.shape[0]

    # outputs_2d_origin -> posenet -> outputs_3d_origin
    fake_pos_pair_loss_origin = get_posenet_loss(inputs_2d, inputs_3d)
    # outputs_2d_ba -> posenet -> outputs_3d_ba
    fake_pos_pair_loss_ba = get_posenet_loss(outputs_2d_ba, outputs_3d_ba)
    # # outputs_2d_rt -> posenet -> outputs_3d_rt
    fake_pos_pair_loss_rt = get_posenet_loss(outputs_2d_rt, outputs_3d_rt)

    # pair up posenet loss
    ##########################################
    hardratio_ba = update_hardratio(args.hardratio_ba_s, args.hardratio_ba, summary.epoch, args.epochs)
    hardratio_rt = update_hardratio(args.hardratio_rt_s, args.hardratio_rt, summary.epoch, args.epochs)

    # get feedback loss
    pos_pair_loss_baToorigin = fix_hardratio(args.hardratio_std_ba, hardratio_ba,
                                             fake_pos_pair_loss_ba, fake_pos_pair_loss_origin,
                                             args.gloss_factordiv_ba, args.gloss_factorfeedback_ba, tag='ba')
    pos_pair_loss_rtToorigin = fix_hardratio(args.hardratio_std_rt, hardratio_rt,
                                             fake_pos_pair_loss_rt, fake_pos_pair_loss_origin,
                                             args.gloss_factordiv_rt, args.gloss_factorfeedback_rt, tag='rt')

    feedback_loss = pos_pair_loss_baToorigin + pos_pair_loss_rtToorigin

    writer.add_scalar('train_G_iter_posenet_feedback/1) pos_pair_loss_origin', fake_pos_pair_loss_origin.mean().item(),
                      summary.train_iter_num)
    writer.add_scalar('train_G_iter_posenet_feedback/2) pos_pair_loss_ba', fake_pos_pair_loss_ba.mean().item(),
                      summary.train_iter_num)
    writer.add_scalar('train_G_iter_posenet_feedback/3) pos_pair_loss_rt', fake_pos_pair_loss_rt.mean().item(),
                      summary.train_iter_num)

    return feedback_loss