code: def _concat_taskaware_classification_datasets_sequentially(
      train_dataset_list: Sequence[ISupportedClassificationDataset],
      test_dataset_list: Sequence[ISupportedClassificationDataset],
  ) -> Tuple[
      TaskAwareSupervisedClassificationDataset,
      TaskAwareSupervisedClassificationDataset,
      List[list],
  ]:
      """
      Concatenates a list of datasets. This is completely different from
      :class:`ConcatDataset`, in which datasets are merged together without
      other processing. Instead, this function re-maps the datasets class IDs.
      For instance:
      let the dataset[0] contain patterns of 3 different classes,
      let the dataset[1] contain patterns of 2 different classes, then class IDs
      will be mapped as follows:
  
      dataset[0] class "0" -> new class ID is "0"
  
      dataset[0] class "1" -> new class ID is "1"
  
      dataset[0] class "2" -> new class ID is "2"
  
      dataset[1] class "0" -> new class ID is "3"
  
      dataset[1] class "1" -> new class ID is "4"
  
      ... -> ...
  
      dataset[-1] class "C-1" -> new class ID is "overall_n_classes-1"
  
      In contrast, using PyTorch ConcatDataset:
  
      dataset[0] class "0" -> ID is "0"
  
      dataset[0] class "1" -> ID is "1"
  
      dataset[0] class "2" -> ID is "2"
  
      dataset[1] class "0" -> ID is "0"
  
      dataset[1] class "1" -> ID is "1"
  
      Note: ``train_dataset_list`` and ``test_dataset_list`` must have the same
      number of datasets.
  
      :param train_dataset_list: A list of training datasets
      :param test_dataset_list: A list of test datasets
  
      :returns: A concatenated dataset.
      """
      remapped_train_datasets: List[
          TaskAwareSupervisedClassificationDataset
      ] = []
      remapped_test_datasets: List[
          TaskAwareSupervisedClassificationDataset
      ] = []
      next_remapped_idx = 0
  
      train_dataset_list_sup = list(
          map(
              _as_taskaware_supervised_classification_dataset,
              train_dataset_list,
          )
      )
      test_dataset_list_sup = list(
          map(
              _as_taskaware_supervised_classification_dataset,
              test_dataset_list,
          )
      )
      del train_dataset_list
      del test_dataset_list
  
      # Obtain the number of classes of each dataset
      classes_per_dataset = [
          _count_unique(
              train_dataset_list_sup[dataset_idx].targets,
              test_dataset_list_sup[dataset_idx].targets,
          )
          for dataset_idx in range(len(train_dataset_list_sup))
      ]
  
      new_class_ids_per_dataset = []
      for dataset_idx in range(len(train_dataset_list_sup)):
          # Get the train and test sets of the dataset
          train_set = train_dataset_list_sup[dataset_idx]
          test_set = test_dataset_list_sup[dataset_idx]
  
          # Get the classes in the dataset
          dataset_classes = set(map(int, train_set.targets))
  
          # The class IDs for this dataset will be in range
          # [n_classes_in_previous_datasets,
          #       n_classes_in_previous_datasets + classes_in_this_dataset)
          new_classes = list(
              range(
                  next_remapped_idx,
                  next_remapped_idx + classes_per_dataset[dataset_idx],
              )
          )
          new_class_ids_per_dataset.append(new_classes)
  
          # AvalancheSubset is used to apply the class IDs transformation.
          # Remember, the class_mapping parameter must be a list in which:
          # new_class_id = class_mapping[original_class_id]
          # Hence, a list of size equal to the maximum class index is created
          # Only elements corresponding to the present classes are remapped
          class_mapping = [-1] * (max(dataset_classes) + 1)
          j = 0
          for i in dataset_classes:
              class_mapping[i] = new_classes[j]
              j += 1
  
          a = _taskaware_classification_subset(
              train_set, class_mapping=class_mapping
          )
  
          # Create remapped datasets and append them to the final list
          remapped_train_datasets.append(
              _taskaware_classification_subset(
                  train_set, class_mapping=class_mapping
              )
          )
          remapped_test_datasets.append(
              _taskaware_classification_subset(
                  test_set, class_mapping=class_mapping
              )
          )
          next_remapped_idx += classes_per_dataset[dataset_idx]
  
      return (
          _concat_taskaware_classification_datasets(
              remapped_train_datasets
          ),
          _concat_taskaware_classification_datasets(
              remapped_test_datasets
          ),
          new_class_ids_per_dataset,
      )
  
imports: from functools import partial
  import torch
  from torch.utils.data.dataset import Subset, ConcatDataset, TensorDataset
  from avalanche.benchmarks.utils.utils import TaskSet, _count_unique, find_common_transforms_group, _init_task_labels, _init_transform_groups, _split_user_def_targets, _split_user_def_task_label, _traverse_supported_dataset
  from avalanche.benchmarks.utils.data import AvalancheDataset
  from avalanche.benchmarks.utils.transform_groups import TransformGroupDef, DefaultTransformGroups, XTransform, YTransform
  from avalanche.benchmarks.utils.data_attribute import DataAttribute
  from avalanche.benchmarks.utils.dataset_utils import SubSequence
  from avalanche.benchmarks.utils.flat_data import ConstantSequence
  from avalanche.benchmarks.utils.dataset_definitions import ISupportedClassificationDataset, ITensorDataset, IDatasetWithTargets
  from typing import List, Any, Sequence, Union, Optional, TypeVar, Callable, Dict, Tuple, Mapping, overload
label_desc: Use image node_id 19 for calling a global function with example usage: _concat_taskaware_classification_datasets_sequentially(train_dataset_list, test_dataset_list) and returns: _concat_taskaware_classification_datasets,  _concat_taskaware_classification_datasets,  new_class_ids_per_dataset