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))
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`.
    
avalanche/benchmarks/scenarios/supervised.py