def concat_detection_datasets(
    datasets: Sequence[SupportedDetectionDataset],
    *,
    transform: Optional[XTransform] = None,
    target_transform: Optional[YTransform] = None,
    transform_groups: Optional[Mapping[str, Tuple[XTransform, YTransform]]] = None,
    initial_transform_group: Optional[str] = None,
    task_labels: Optional[Union[int, Sequence[int], Sequence[Sequence[int]]]] = None,
    targets: Optional[
        Union[Sequence[TTargetType], Sequence[Sequence[TTargetType]]]
    ] = None,
    collate_fn: Optional[Callable[[List], Any]] = None
) -> Union[DetectionDataset, SupervisedDetectionDataset]:
    """Creates a ``AvalancheConcatDataset`` instance.

    For simple subset operations you should use the method
    `dataset.concat(other)` or
    `concat_datasets` from `avalanche.benchmarks.utils.utils`.
    Use this constructor only if you need to redefine transformation or
    class/task labels.

    A Dataset that behaves like a PyTorch
    :class:`torch.utils.data.ConcatDataset`. However, this Dataset also supports
    transformations, slicing, advanced indexing and the targets field and all
    the other goodies listed in :class:`AvalancheDataset`.

    This dataset guarantees that the operations involving the transformations
    and transformations groups are consistent across the concatenated dataset
    (if they are subclasses of :class:`AvalancheDataset`).

    :param datasets: A collection of datasets.
    :param transform: A function/transform that takes the X value of a
        pattern from the original dataset and returns a transformed version.
    :param target_transform: A function/transform that takes in the target
        and transforms it.
    :param transform_groups: A dictionary containing the transform groups.
        Transform groups are used to quickly switch between training and
        eval (test) transformations. This becomes useful when in need to
        test on the training dataset as test transformations usually don't
        contain random augmentations. ``AvalancheDataset`` natively supports
        the 'train' and 'eval' groups by calling the ``train()`` and
        ``eval()`` methods. When using custom groups one can use the
        ``with_transforms(group_name)`` method instead. Defaults to None,
        which means that the current transforms will be used to
        handle both 'train' and 'eval' groups (just like in standard
        ``torchvision`` datasets).
    :param initial_transform_group: The name of the initial transform group
        to be used. Defaults to None, which means that if all
        AvalancheDatasets in the input datasets list agree on a common
        group (the "current group" is the same for all datasets), then that
        group will be used as the initial one. If the list of input datasets
        does not contain an AvalancheDataset or if the AvalancheDatasets
        do not agree on a common group, then 'train' will be used.
    :param targets: The label of each pattern. Can either be a sequence of
        labels or, alternatively, a sequence containing sequences of labels
        (one for each dataset to be concatenated). Defaults to None, which
        means that the targets will be retrieved from the datasets (if
        possible).
    :param task_labels: The task labels for each pattern. Must be a sequence
        of ints, one for each pattern in the dataset. Alternatively, task
        labels can be expressed as a sequence containing sequences of ints
        (one for each dataset to be concatenated) or even a single int,
        in which case that value will be used as the task label for all
        instances. Defaults to None, which means that the dataset will try
        to obtain the task labels from the original datasets. If no task
        labels could be found for a dataset, a default task label 0 will
        be applied to all patterns of that dataset.
    :param collate_fn: The function to use when slicing to merge single
        patterns. This function is the function used in the data loading
        process, too. If None, the constructor will check if a `collate_fn`
        field exists in the first dataset. If no such field exists, the
        default collate function for detection  will be used.
        Beware that the chosen collate function will be applied to all
        the concatenated datasets even if a different collate is defined
        in different datasets.
    """
    dds = []
    per_dataset_task_labels = _split_user_def_task_label(datasets, task_labels)

    per_dataset_targets = _split_user_def_targets(
        datasets, targets, lambda x: isinstance(x, dict)
    )

    for dd, dataset_task_labels, dataset_targets in zip(
        datasets, per_dataset_task_labels, per_dataset_targets
    ):
        dd = make_detection_dataset(
            dd,
            transform=transform,
            target_transform=target_transform,
            transform_groups=transform_groups,
            initial_transform_group=initial_transform_group,
            task_labels=dataset_task_labels,
            targets=dataset_targets,
            collate_fn=collate_fn,
        )
        dds.append(dd)

    if (
        transform is None
        and target_transform is None
        and transform_groups is None
        and initial_transform_group is None
        and task_labels is None
        and targets is None
        and collate_fn is None
        and len(datasets) > 0
    ):
        d0 = datasets[0]
        if isinstance(d0, DetectionDataset):
            for d1 in datasets[1:]:
                d0 = d0.concat(d1)
            return d0

    das: List[DataAttribute] = []
    if len(dds) > 0:
        #######################################
        # TRANSFORMATION GROUPS
        #######################################
        transform_groups_obj = _init_transform_groups(
            transform_groups,
            transform,
            target_transform,
            initial_transform_group,
            dds[0],
        )

        # Find common "current_group" or use "train"
        if initial_transform_group is None:
            uniform_group = None
            for d_set in datasets:
                if isinstance(d_set, AvalancheDataset):
                    if uniform_group is None:
                        uniform_group = d_set._transform_groups.current_group
                    else:
                        if uniform_group != d_set._transform_groups.current_group:
                            uniform_group = None
                            break

            if uniform_group is None:
                initial_transform_group = "train"
            else:
                initial_transform_group = uniform_group

        #######################################
        # DATA ATTRIBUTES
        #######################################

        totlen = sum([len(d) for d in datasets])
        if task_labels is not None:  # User defined targets always take precedence
            all_labels: IDataset[int]
            if isinstance(task_labels, int):
                all_labels = ConstantSequence(task_labels, totlen)
            else:
                all_labels_lst = []
                for dd, dataset_task_labels in zip(dds, per_dataset_task_labels):
                    assert dataset_task_labels is not None

                    # We already checked that len(t_labels) == len(dataset)
                    # (done in _split_user_def_task_label)
                    if isinstance(dataset_task_labels, int):
                        all_labels_lst.extend([dataset_task_labels] * len(dd))
                    else:
                        all_labels_lst.extend(dataset_task_labels)
                all_labels = all_labels_lst
            das.append(
                DataAttribute(all_labels, "targets_task_labels", use_in_getitem=True)
            )

        if targets is not None:  # User defined targets always take precedence
            all_targets_lst: List[TTargetType] = []
            for dd, dataset_targets in zip(dds, per_dataset_targets):
                assert dataset_targets is not None

                # We already checked that len(targets) == len(dataset)
                # (done in _split_user_def_targets)
                all_targets_lst.extend(dataset_targets)
            das.append(DataAttribute(all_targets_lst, "targets"))
    else:
        transform_groups_obj = None
        initial_transform_group = "train"

    data = DetectionDataset(
        dds,
        transform_groups=transform_groups_obj,
        data_attributes=das if len(das) > 0 else None,
    )
    return data.with_transforms(initial_transform_group)
Creates a ``AvalancheConcatDataset`` instance.

    For simple subset operations you should use the method
    `dataset.concat(other)` or
    `concat_datasets` from `avalanche.benchmarks.utils.utils`.
    Use this constructor only if you need to redefine transformation or
    class/task labels.

    A Dataset that behaves like a PyTorch
    :class:`torch.utils.data.ConcatDataset`. However, this Dataset also supports
    transformations, slicing, advanced indexing and the targets field and all
    the other goodies listed in :class:`AvalancheDataset`.

    This dataset guarantees that the operations involving the transformations
    and transformations groups are consistent across the concatenated dataset
    (if they are subclasses of :class:`AvalancheDataset`).

    :param datasets: A collection of datasets.
    :param transform: A function/transform that takes the X value of a
        pattern from the original dataset and returns a transformed version.
    :param target_transform: A function/transform that takes in the target
        and transforms it.
    :param transform_groups: A dictionary containing the transform groups.
        Transform groups are used to quickly switch between training and
        eval (test) transformations. This becomes useful when in need to
        test on the training dataset as test transformations usually don't
        contain random augmentations. ``AvalancheDataset`` natively supports
        the 'train' and 'eval' groups by calling the ``train()`` and
        ``eval()`` methods. When using custom groups one can use the
        ``with_transforms(group_name)`` method instead. Defaults to None,
        which means that the current transforms will be used to
        handle both 'train' and 'eval' groups (just like in standard
        ``torchvision`` datasets).
    :param initial_transform_group: The name of the initial transform group
        to be used. Defaults to None, which means that if all
        AvalancheDatasets in the input datasets list agree on a common
        group (the "current group" is the same for all datasets), then that
        group will be used as the initial one. If the list of input datasets
        does not contain an AvalancheDataset or if the AvalancheDatasets
        do not agree on a common group, then 'train' will be used.
    :param targets: The label of each pattern. Can either be a sequence of
        labels or, alternatively, a sequence containing sequences of labels
        (one for each dataset to be concatenated). Defaults to None, which
        means that the targets will be retrieved from the datasets (if
        possible).
    :param task_labels: The task labels for each pattern. Must be a sequence
        of ints, one for each pattern in the dataset. Alternatively, task
        labels can be expressed as a sequence containing sequences of ints
        (one for each dataset to be concatenated) or even a single int,
        in which case that value will be used as the task label for all
        instances. Defaults to None, which means that the dataset will try
        to obtain the task labels from the original datasets. If no task
        labels could be found for a dataset, a default task label 0 will
        be applied to all patterns of that dataset.
    :param collate_fn: The function to use when slicing to merge single
        patterns. This function is the function used in the data loading
        process, too. If None, the constructor will check if a `collate_fn`
        field exists in the first dataset. If no such field exists, the
        default collate function for detection  will be used.
        Beware that the chosen collate function will be applied to all
        the concatenated datasets even if a different collate is defined
        in different datasets.
    
avalanche/benchmarks/utils/detection_dataset.py