def _setup_for_train(
        self,
        train_dataset: TorchTrainingDataset,
        val_dataset: Optional[TorchTrainingDataset] = None,
        trainer: Optional[pl.Trainer] = None,
        verbose: Optional[bool] = None,
        epochs: int = 0,
        dataloader_kwargs: Optional[dict[str, Any]] = None,
    ) -> tuple[pl.Trainer, PLForecastingModule, DataLoader, Optional[DataLoader]]:
        """This method acts on `TorchTrainingDataset` inputs. It performs sanity checks, and sets up / returns the
        trainer, model, and dataset loaders required for training the model with `_train()`.
        """
        self._verify_train_dataset_type(train_dataset)

        # proactively catch length exceptions to display nicer messages
        train_length_ok, val_length_ok = True, True
        try:
            len(train_dataset)
        except ValueError:
            train_length_ok = False
        if val_dataset is not None:
            try:
                len(val_dataset)
            except ValueError:
                val_length_ok = False

        raise_if(
            not train_length_ok or len(train_dataset) == 0,  # mind the order
            "The provided training time series dataset is too short for obtaining even one training point.",
            logger,
        )
        raise_if(
            val_dataset is not None and (not val_length_ok or len(val_dataset) == 0),
            "The provided validation time series dataset is too short for obtaining even one training point.",
            logger,
        )

        train_sample = train_dataset[0]
        # ignore sample weights [-2] for model dimensions
        train_sample_no_weight = train_sample[:-2] + train_sample[-1:]
        if self.model is None:
            # build model based on the dimensions of the first series in the train set.
            self.train_sample = train_sample_no_weight
            self.output_dim = train_sample[-1].shape[1]
            model = self._init_model(trainer)
        else:
            model = self.model
            # check existing model has input/output dims matching what's provided in the training set.
            raise_if_not(
                len(train_sample_no_weight) == len(self.train_sample),
                "The size of the training set samples (tuples) does not match what the model has been"
                f" previously trained on. Trained on tuples of length {len(self.train_sample)},"
                f" received tuples of length {len(train_sample_no_weight)}.",
            )
            sample_shapes_last = [
                s.shape[1] if s is not None else None for s in self.train_sample
            ]
            sample_shapes = [
                s.shape[1] if s is not None else None for s in train_sample_no_weight
            ]
            raise_if_not(
                sample_shapes == sample_shapes_last,
                "The dimensionality of the series in the training set do not match the dimensionality"
                " of the series the model has previously been trained on. "
                f"Model input/output dimensions = {sample_shapes_last},"
                f" provided input/output dimensions = {sample_shapes}",
            )

        # loss must not reduce the output when using sample weight
        train_sample_weight = train_sample[-2]
        val_sample_weight = val_dataset[0][-2] if val_dataset is not None else None
        for sample_weight, criterion, set_name in [
            (train_sample_weight, model.train_criterion, "train"),
            (val_sample_weight, model.val_criterion, "val"),
        ]:
            if criterion is None or sample_weight is None:
                continue

            # we need to check that loss has a reduction param that we can change when calling
            # `fit()` with sample weights
            if not hasattr(criterion, "reduction"):
                raise_log(
                    ValueError(
                        "torch loss function `loss_fn` must have an attribute `reduction` which controls how "
                        "to reduce the loss over each batch. With `reduction='none'` it must not reduce the loss."
                    ),
                    logger=logger,
                )

            # remember the original reduction (reset in `PLForecastingModule.on_fit_end()`
            if set_name == "train":
                model.train_criterion_reduction = criterion.reduction
            else:
                model.val_criterion_reduction = criterion.reduction
            # overwrite criterion to not reduce the loss for sample weights
            criterion.reduction = "none"

            shape_out = (2, 2)
            loss = criterion(torch.ones(shape_out), torch.zeros(shape_out))
            if not loss.shape == shape_out:
                raise_log(
                    ValueError(
                        "Failed to make `loss_fn` not reduce the loss output when using `(val)_sample_weight`. "
                        "The loss function `loss_fn` must have an attribute `reduction` which when setting it to "
                        "`'none'`, must not reduce the output."
                    ),
                    logger=logger,
                )

        # setting drop_last to False makes the model see each sample at least once, and guarantee the presence of at
        # least one batch no matter the chosen batch size
        dataloader_kwargs = dict(
            {
                "batch_size": self.batch_size,
                "shuffle": True,
                "pin_memory": True,
                "drop_last": False,
                "collate_fn": self._batch_collate_fn,
            },
            **(dataloader_kwargs or dict()),
        )

        train_loader = DataLoader(
            train_dataset,
            **dataloader_kwargs,
        )

        # prepare validation data
        dataloader_kwargs["shuffle"] = False
        val_loader = (
            None
            if val_dataset is None
            else DataLoader(
                val_dataset,
                **dataloader_kwargs,
            )
        )

        # if user wants to train the model for more epochs, ignore the n_epochs parameter
        train_num_epochs = epochs if epochs > 0 else self.n_epochs

        # setup trainer
        trainer = self._setup_trainer(trainer, model, verbose, train_num_epochs)

        if model.epochs_trained > 0 and not self.load_ckpt_path:
            logger.warning(
                f"Attempting to retrain/fine-tune the model without resuming from a checkpoint. This is currently "
                f"discouraged. Consider model `{self.__class__.__name__}.load_weights()` to load the weights for "
                f"fine-tuning."
            )
        return trainer, model, train_loader, val_loader
This method acts on `TorchTrainingDataset` inputs. It performs sanity checks, and sets up / returns the
        trainer, model, and dataset loaders required for training the model with `_train()`.
        
darts/models/forecasting/torch_forecasting_model.py