def prediction_tractogram(hyperparams, model, dataset, batch_size_override,
                          prediction_method):
    loss = loss_factory(hyperparams,
                        model,
                        dataset,
                        loss_type=prediction_method)
    batch_scheduler = batch_scheduler_factory(
        hyperparams,
        dataset,
        train_mode=False,
        batch_size_override=batch_size_override,
        use_data_augment=False)

    _ = loss.losses  # Hack to generate update dict in loss :(
    predictions = loss.samples

    predict, timestep_losses, inputs, targets, masks = log_variables(
        batch_scheduler, model, predictions, loss.loss_per_time_step,
        dataset.symb_inputs * 1, dataset.symb_targets * 1,
        dataset.symb_mask * 1)
    if hyperparams['model'] == 'ffnn_regression':
        # Regrouping data into streamlines will only work if the original streamlines were NOT shuffled, resampled or augmented
        timesteps_prediction = ArraySequence()
        timesteps_loss = ArraySequence()
        timesteps_inputs = ArraySequence()
        timesteps_targets = ArraySequence()
        idx = 0
        for length in dataset.streamlines._lengths:
            start = idx
            idx = end = idx + length
            timesteps_prediction.extend(predict[start:end])
            timesteps_loss.extend(timestep_losses[start:end])
            timesteps_inputs.extend(inputs[start:end])
            timesteps_targets.extend(targets[start:end])
    else:
        timesteps_prediction = ArraySequence(
            [p[:int(m.sum())] for p, m in zip(chain(*predict), chain(*masks))])
        timesteps_loss = ArraySequence([
            l[:int(m.sum())]
            for l, m in zip(chain(*timestep_losses), chain(*masks))
        ])
        timesteps_inputs = ArraySequence(
            [i[:int(m.sum())] for i, m in zip(chain(*inputs), chain(*masks))])
        # Use np.squeeze in case gru_multistep is used to remove the empty k=1 dimension
        timesteps_targets = ArraySequence([
            np.squeeze(t[:int(m.sum())])
            for t, m in zip(chain(*targets), chain(*masks))
        ])

    # Debug : Print norm stats
    # print("Dataset: {}; # of streamlines: {}".format(dataset.name, len(dataset)))
    # all_predictions = np.array(list(chain(*timesteps_prediction)))
    # prediction_norms = np.linalg.norm(all_predictions, axis=1)
    # print("Prediction norm --- Mean:{}; Max:{}; Min:{}".format(np.mean(prediction_norms), np.max(prediction_norms), np.min(prediction_norms)))
    # all_targets = np.array(list(chain(*timesteps_targets)))
    # target_norms = np.linalg.norm(all_targets, axis=1)
    # print("Target norm --- Mean:{}; Max:{}; Min:{}".format(np.mean(target_norms), np.max(target_norms), np.min(target_norms)))

    # Color is based on timestep loss
    cmap = cm.get_cmap('bwr')
    values = np.concatenate(timesteps_loss)
    vmin = np.percentile(values, 5)
    vmax = np.percentile(values, 95)
    scalar_map = cm.ScalarMappable(norm=mplcolors.Normalize(vmin=vmin,
                                                            vmax=vmax),
                                   cmap=cmap)

    streamlines = []
    colors = []

    for i, t, p, l in zip(timesteps_inputs, timesteps_targets,
                          timesteps_prediction, timesteps_loss):
        pts = np.r_[i[:, :3], [i[-1, :3] + t[-1]]]

        streamline = np.zeros(((len(pts) - 1) * 3 + 1, 3))
        streamline[::3] = pts
        streamline[1:-1:3] = pts[:-1] + p
        streamline[2:-1:3] = pts[:-1]
        streamlines.append(streamline)

        # Color input streamlines in a uniform color, then color predictions based on L2 error
        color = np.zeros_like(streamline)

        # Base color of streamlines is minimum value (best score)
        color[:] = scalar_map.to_rgba(vmin, bytes=True)[:3]
        color[1:-1:3, :] = scalar_map.to_rgba(l, bytes=True)[:, :3]
        colors.append(color)

    tractogram = nib.streamlines.Tractogram(streamlines,
                                            data_per_point={"colors": colors})
    return tractogram
def evaluation_tractogram(hyperparams, model, dataset, batch_size_override, metric):
    loss = loss_factory(hyperparams, model, dataset, loss_type=None)
    batch_scheduler = batch_scheduler_factory(hyperparams, dataset, train_mode=False, batch_size_override=batch_size_override, use_data_augment=False)

    _ = loss.losses  # Hack to generate update dict in loss :(

    if hyperparams['model'] == 'ffnn_regression':
        timestep_losses, inputs, targets = log_variables(batch_scheduler,
                                                         model,
                                                         loss.loss_per_time_step,
                                                         dataset.symb_inputs * 1,
                                                         dataset.symb_targets * 1)
        # Regrouping data into streamlines will only work if the original streamlines were NOT shuffled, resampled or augmented
        timesteps_loss = ArraySequence()
        seq_loss = []
        timesteps_inputs = ArraySequence()
        timesteps_targets = ArraySequence()
        idx = 0
        for length in dataset.streamlines._lengths:
            start = idx
            idx = end = idx+length
            timesteps_loss.extend(timestep_losses[start:end])
            seq_loss.extend(np.mean(timestep_losses[start:end]))
            timesteps_inputs.extend(inputs[start:end])
            timesteps_targets.extend(targets[start:end])
    else:

        timestep_losses, seq_losses, inputs, targets, masks = log_variables(batch_scheduler,
                                                                            model,
                                                                            loss.loss_per_time_step,
                                                                            loss.loss_per_seq,
                                                                            dataset.symb_inputs * 1,
                                                                            dataset.symb_targets * 1,
                                                                            dataset.symb_mask * 1)
        timesteps_loss = ArraySequence([l[:int(m.sum())] for l, m in zip(chain(*timestep_losses), chain(*masks))])
        seq_loss = np.array(list(chain(*seq_losses)))
        timesteps_inputs = ArraySequence([i[:int(m.sum())] for i, m in zip(chain(*inputs), chain(*masks))])
        # Use np.squeeze in case gru_multistep is used to remove the empty k=1 dimension
        timesteps_targets = ArraySequence([np.squeeze(t[:int(m.sum())]) for t, m in zip(chain(*targets), chain(*masks))])

    if metric == 'sequence':
        # Color is based on sequence loss
        values = seq_loss
    elif metric == 'timestep' or metric == 'cumul_avg':
        # Color is based on timestep loss
        values = np.concatenate(timesteps_loss)
    else:
        raise ValueError("Unrecognized metric: {}".format(metric))

    cmap = cm.get_cmap('bwr')
    vmin = np.percentile(values, 5)
    vmax = np.percentile(values, 95)
    scalar_map = cm.ScalarMappable(norm=mplcolors.Normalize(vmin=vmin, vmax=vmax), cmap=cmap)

    streamlines = []
    colors = []

    for i, t, l, seq_l in zip(timesteps_inputs, timesteps_targets, timesteps_loss, seq_loss):
        pts = np.r_[i[:, :3], [i[-1, :3] + t[-1]]]

        color = np.zeros_like(pts)
        if metric == 'sequence':
            # Streamline color is based on sequence loss
            color[:, :] = scalar_map.to_rgba(seq_l, bytes=True)[:3]
        elif metric == 'timestep':
            # Streamline color is based on timestep loss
            # Identify first point with green
            color[0, :] = [0, 255, 0]
            color[1:, :] = scalar_map.to_rgba(l, bytes=True)[:, :3]
        elif metric == 'cumul_avg':
            # Streamline color is based on timestep loss

            # Compute cumulative average
            cumul_avg = np.cumsum(l) / np.arange(1, len(l) + 1)

            # Identify first point with green
            color[0, :] = [0, 255, 0]
            color[1:, :] = scalar_map.to_rgba(cumul_avg, bytes=True)[:, :3]
        else:
            raise ValueError("Unrecognized metric: {}".format(metric))

        streamlines.append(pts)
        colors.append(color)

    tractogram = nib.streamlines.Tractogram(streamlines, data_per_point={"colors": colors})
    return tractogram
def evaluation_tractogram(hyperparams, model, dataset, batch_size_override,
                          metric):
    loss = loss_factory(hyperparams, model, dataset, loss_type=None)
    batch_scheduler = batch_scheduler_factory(
        hyperparams,
        dataset,
        train_mode=False,
        batch_size_override=batch_size_override,
        use_data_augment=False)

    _ = loss.losses  # Hack to generate update dict in loss :(

    if hyperparams['model'] == 'ffnn_regression':
        timestep_losses, inputs, targets = log_variables(
            batch_scheduler, model, loss.loss_per_time_step,
            dataset.symb_inputs * 1, dataset.symb_targets * 1)
        # Regrouping data into streamlines will only work if the original streamlines were NOT shuffled, resampled or augmented
        timesteps_loss = ArraySequence()
        seq_loss = []
        timesteps_inputs = ArraySequence()
        timesteps_targets = ArraySequence()
        idx = 0
        for length in dataset.streamlines._lengths:
            start = idx
            idx = end = idx + length
            timesteps_loss.extend(timestep_losses[start:end])
            seq_loss.extend(np.mean(timestep_losses[start:end]))
            timesteps_inputs.extend(inputs[start:end])
            timesteps_targets.extend(targets[start:end])
    else:

        timestep_losses, seq_losses, inputs, targets, masks = log_variables(
            batch_scheduler, model, loss.loss_per_time_step, loss.loss_per_seq,
            dataset.symb_inputs * 1, dataset.symb_targets * 1,
            dataset.symb_mask * 1)
        timesteps_loss = ArraySequence([
            l[:int(m.sum())]
            for l, m in zip(chain(*timestep_losses), chain(*masks))
        ])
        seq_loss = np.array(list(chain(*seq_losses)))
        timesteps_inputs = ArraySequence(
            [i[:int(m.sum())] for i, m in zip(chain(*inputs), chain(*masks))])
        # Use np.squeeze in case gru_multistep is used to remove the empty k=1 dimension
        timesteps_targets = ArraySequence([
            np.squeeze(t[:int(m.sum())])
            for t, m in zip(chain(*targets), chain(*masks))
        ])

    if metric == 'sequence':
        # Color is based on sequence loss
        values = seq_loss
    elif metric == 'timestep' or metric == 'cumul_avg':
        # Color is based on timestep loss
        values = np.concatenate(timesteps_loss)
    else:
        raise ValueError("Unrecognized metric: {}".format(metric))

    cmap = cm.get_cmap('bwr')
    vmin = np.percentile(values, 5)
    vmax = np.percentile(values, 95)
    scalar_map = cm.ScalarMappable(norm=mplcolors.Normalize(vmin=vmin,
                                                            vmax=vmax),
                                   cmap=cmap)

    streamlines = []
    colors = []

    for i, t, l, seq_l in zip(timesteps_inputs, timesteps_targets,
                              timesteps_loss, seq_loss):
        pts = np.r_[i[:, :3], [i[-1, :3] + t[-1]]]

        color = np.zeros_like(pts)
        if metric == 'sequence':
            # Streamline color is based on sequence loss
            color[:, :] = scalar_map.to_rgba(seq_l, bytes=True)[:3]
        elif metric == 'timestep':
            # Streamline color is based on timestep loss
            # Identify first point with green
            color[0, :] = [0, 255, 0]
            color[1:, :] = scalar_map.to_rgba(l, bytes=True)[:, :3]
        elif metric == 'cumul_avg':
            # Streamline color is based on timestep loss

            # Compute cumulative average
            cumul_avg = np.cumsum(l) / np.arange(1, len(l) + 1)

            # Identify first point with green
            color[0, :] = [0, 255, 0]
            color[1:, :] = scalar_map.to_rgba(cumul_avg, bytes=True)[:, :3]
        else:
            raise ValueError("Unrecognized metric: {}".format(metric))

        streamlines.append(pts)
        colors.append(color)

    tractogram = nib.streamlines.Tractogram(streamlines,
                                            data_per_point={"colors": colors})
    return tractogram
def prediction_tractogram(hyperparams, model, dataset, batch_size_override, prediction_method):
    loss = loss_factory(hyperparams, model, dataset, loss_type=prediction_method)
    batch_scheduler = batch_scheduler_factory(hyperparams, dataset, train_mode=False, batch_size_override=batch_size_override, use_data_augment=False)

    _ = loss.losses  # Hack to generate update dict in loss :(
    predictions = loss.samples

    predict, timestep_losses, inputs, targets, masks = log_variables(batch_scheduler,
                                                                     model,
                                                                     predictions,
                                                                     loss.loss_per_time_step,
                                                                     dataset.symb_inputs * 1,
                                                                     dataset.symb_targets * 1,
                                                                     dataset.symb_mask * 1)
    if hyperparams['model'] == 'ffnn_regression':
        # Regrouping data into streamlines will only work if the original streamlines were NOT shuffled, resampled or augmented
        timesteps_prediction = ArraySequence()
        timesteps_loss = ArraySequence()
        timesteps_inputs = ArraySequence()
        timesteps_targets = ArraySequence()
        idx = 0
        for length in dataset.streamlines._lengths:
            start = idx
            idx = end = idx+length
            timesteps_prediction.extend(predict[start:end])
            timesteps_loss.extend(timestep_losses[start:end])
            timesteps_inputs.extend(inputs[start:end])
            timesteps_targets.extend(targets[start:end])
    else:
        timesteps_prediction = ArraySequence([p[:int(m.sum())] for p, m in zip(chain(*predict), chain(*masks))])
        timesteps_loss = ArraySequence([l[:int(m.sum())] for l, m in zip(chain(*timestep_losses), chain(*masks))])
        timesteps_inputs = ArraySequence([i[:int(m.sum())] for i, m in zip(chain(*inputs), chain(*masks))])
        # Use np.squeeze in case gru_multistep is used to remove the empty k=1 dimension
        timesteps_targets = ArraySequence([np.squeeze(t[:int(m.sum())]) for t, m in zip(chain(*targets), chain(*masks))])

    # Debug : Print norm stats
    # print("Dataset: {}; # of streamlines: {}".format(dataset.name, len(dataset)))
    # all_predictions = np.array(list(chain(*timesteps_prediction)))
    # prediction_norms = np.linalg.norm(all_predictions, axis=1)
    # print("Prediction norm --- Mean:{}; Max:{}; Min:{}".format(np.mean(prediction_norms), np.max(prediction_norms), np.min(prediction_norms)))
    # all_targets = np.array(list(chain(*timesteps_targets)))
    # target_norms = np.linalg.norm(all_targets, axis=1)
    # print("Target norm --- Mean:{}; Max:{}; Min:{}".format(np.mean(target_norms), np.max(target_norms), np.min(target_norms)))

    # Color is based on timestep loss
    cmap = cm.get_cmap('bwr')
    values = np.concatenate(timesteps_loss)
    vmin = np.percentile(values, 5)
    vmax = np.percentile(values, 95)
    scalar_map = cm.ScalarMappable(norm=mplcolors.Normalize(vmin=vmin, vmax=vmax), cmap=cmap)

    streamlines = []
    colors = []

    for i, t, p, l in zip(timesteps_inputs, timesteps_targets, timesteps_prediction, timesteps_loss):
        pts = np.r_[i[:, :3], [i[-1, :3] + t[-1]]]

        streamline = np.zeros(((len(pts) - 1) * 3 + 1, 3))
        streamline[::3] = pts
        streamline[1:-1:3] = pts[:-1] + p
        streamline[2:-1:3] = pts[:-1]
        streamlines.append(streamline)

        # Color input streamlines in a uniform color, then color predictions based on L2 error
        color = np.zeros_like(streamline)

        # Base color of streamlines is minimum value (best score)
        color[:] = scalar_map.to_rgba(vmin, bytes=True)[:3]
        color[1:-1:3, :] = scalar_map.to_rgba(l, bytes=True)[:, :3]
        colors.append(color)

    tractogram = nib.streamlines.Tractogram(streamlines, data_per_point={"colors": colors})
    return tractogram