code: def class_incremental_benchmark(
      datasets_dict: Dict[str, ClassificationDataset],
      *,
      class_order: Optional[Sequence[int]] = None,
      num_experiences: Optional[int] = None,
      num_classes_per_exp: Optional[Sequence[int]] = None,
      seed: Optional[int] = None,
  ) -> CLScenario:
      """Splits datasets according to a class-incremental scenario.
  
      Each dataset will create a stream with the same class order.
  
      :param datasets_dict: A dictionary with stream names as keys (str) and
          AvalancheDataset as values. Usually, you want to provide at least train
          and test stream.
      :param class_order: List of classes that determine the order of appearance
          in the stream. If `None`, random classes will be used.
          Defaults to None (random classes).
      :param num_experiences: desired number of experiences in the stream.
      :param num_classes_per_exp: If not None, a list with the number of classes
          to pick for each experience.
      :param seed: The seed to use for random shuffling if `class_order is None`.
          If None, the current PyTorch random number generator state will be used.
          Defaults to None.
  
      :return: A class-incremental :class:`CLScenario`.
      """
      if (class_order is not None) and (seed is not None):
          raise ValueError(
              "Can't set `seed` if a fixed `class_order` is given."
          )
      if (num_classes_per_exp is not None) and (
          num_experiences is not None
      ):
          raise ValueError(
              "Only one of `num_classes_per_exp` or `num_experiences` can be used."
          )
      if (num_classes_per_exp is None) and (num_experiences is None):
          raise ValueError(
              "One of `num_classes_per_exp` or `num_experiences` must be set."
          )
      if num_experiences is not None and num_experiences < 1:
          raise ValueError(
              "Invalid number of experiences (n_experiences "
              "parameter): must be greater than 0"
          )
  
      # convert to avalanche datasets
      for name, dd in datasets_dict.items():
          if not isinstance(dd, AvalancheDataset):
              datasets_dict[
                  name
              ] = _as_taskaware_supervised_classification_dataset(dd)
  
      # validate classes
      dd_classes = list(datasets_dict.values())[0].targets.uniques
      num_classes: int = 1 + max(
          list(datasets_dict.values())[0].targets.uniques
      )
      if (num_classes_per_exp is not None) and (
          num_classes != sum(num_classes_per_exp)
      ):
          raise ValueError(
              "`sum(num_classes_per_exp)` must be equal to the total number of classes."
          )
      for (
          dd
      ) in datasets_dict.values():  # all datasets have the same classes
          clss = dd.targets.uniques
          if dd_classes != clss:
              raise ValueError(
                  "`datasets` must all have the same classes"
              )
  
      # pick random class order if needed
      if class_order is None:  # sample random class order
          if seed is not None:
              torch.random.manual_seed(seed)
          class_order = torch.randperm(num_classes).tolist()
  
      # split classes by experience
      classes_exp_assignment = []
      if num_experiences is not None:
          assert (
              num_classes_per_exp is None
          ), "BUG: num_classes_per_exp must be None"
          curr_classess_per_exp: int = num_classes // num_experiences
          for eid in range(num_experiences):
              if eid == 0:
                  classes_exp_assignment.append(
                      class_order[:curr_classess_per_exp]
                  )
              else:
                  # final exp will take reminder of classes if they don't divide equally
                  start_idx = curr_classess_per_exp * eid
                  end_idx = start_idx + curr_classess_per_exp
                  classes_exp_assignment.append(
                      class_order[start_idx:end_idx]
                  )
      elif num_classes_per_exp is not None:
          num_curr = 0
          for eid, num_classes in enumerate(num_classes_per_exp):
              curr_classes = class_order[
                  num_curr : num_curr + num_classes
              ]
              classes_exp_assignment.append(curr_classes)
              num_curr += num_classes
  
      # create the streams using class_order to split the data
      streams = []
      for name, dd in datasets_dict.items():
          curr_stream = []
          data_by_class = _split_dataset_by_attribute(dd, "targets")
          for eid, clss in enumerate(classes_exp_assignment):
              curr_data: ClassificationDataset = ClassificationDataset(
                  [], data_attributes=[DataAttribute([], "targets")]
              )
              for cls in clss:
                  # TODO: curr_data.concat(data_by_class[cls]) is bugged and removes targets
                  curr_data = data_by_class[cls].concat(curr_data)
              curr_stream.append(DatasetExperience(dataset=curr_data))
          streams.append(EagerCLStream(name, curr_stream))
      return with_classes_timeline(CLScenario(streams))
  
imports: import warnings
  from copy import copy
  from typing import Iterable, Sequence, Optional, Dict, List, Protocol
  import torch
  from avalanche.benchmarks.utils.classification_dataset import ClassificationDataset, _as_taskaware_supervised_classification_dataset, TaskAwareSupervisedClassificationDataset
  from avalanche.benchmarks.utils.data import AvalancheDataset
  from avalanche.benchmarks.utils.data_attribute import DataAttribute
  from .dataset_scenario import _split_dataset_by_attribute, DatasetExperience
  from ..None import CLScenario, CLStream, EagerCLStream
label_desc: Use image node_id 1 for calling a global function with example usage: class_incremental_benchmark(datasets_dict) and returns: with_classes_timeline