import torch


def get_current_targets(task_id, hnet):
    """
    For all :math:`j < \text{task\_id}`, compute the output of the
    hypernetwork. This output will be detached from the graph and cloned before
    being added to the return list of this function.
    
    Note, if these targets don't change during training, it would be more memory
    efficient to store the weights :math:`\theta^*` of the hypernetwork (which
    is a fixed amount of memory compared to the variable number of tasks).
    Though, it is more computationally expensive to recompute
    :math:`h(c_j, \theta^*)` for all :math:`j < \text{task\_id}` everytime the
    target is needed.
    
    Note, this function sets the hypernet temporarily in eval mode. No gradients
    are computed.
    
    Args:
        task_id: The ID of the current task.
        hnet: An instance of the hypernetwork before learning a new task
            (i.e., the hypernetwork has the weights :math:`\theta^*` necessary
            to compute the targets).
    Returns:
        An empty list, if `task_id` is 0. Otherwise, a list of `task_id`-1
        targets. These targets can be passed to the method
        :func:`calc_fix_target_reg` while training on the new task.
    """
    # We temporarily switch to eval mode for target computation (e.g., to get
    # rid of training stochasticities such as dropout).
    hnet_mode = hnet.training
    hnet.eval()

    ret = []

    with torch.no_grad():
        for j in range(task_id):
            W = hnet.forward(task_id=j)
            ret.append([d.detach().clone() for d in W])

    hnet.train(mode=hnet_mode)

    return ret