microsoft/qlib · error · NotImplementedError

mode {} is not supported!

Error message

mode {} is not supported!

What it means

Thrown by TCTSModel.loss_fn when scoring predictions against labels. The `mode` hyperparameter selects how the label-weighting head is applied: 'hard' (argmax over per-step weights) or 'soft' (weighted average over future steps). Any other value falls to the else-branch NotImplementedError at fit time.

Source

Thrown at qlib/contrib/model/pytorch_tcts.py:132

                loss,
                GPU,
                self.use_gpu,
                seed,
            )
        )

    def loss_fn(self, pred, label, weight):
        if self.mode == "hard":
            loc = torch.argmax(weight, 1)
            loss = (pred - label[np.arange(weight.shape[0]), loc]) ** 2
            return torch.mean(loss)

        elif self.mode == "soft":
            loss = (pred - label.transpose(0, 1)) ** 2
            return torch.mean(loss * weight.transpose(0, 1))

        else:
            raise NotImplementedError("mode {} is not supported!".format(self.mode))

    def train_epoch(self, x_train, y_train, x_valid, y_valid):
        x_train_values = x_train.values
        y_train_values = np.squeeze(y_train.values)

        indices = np.arange(len(x_train_values))
        np.random.shuffle(indices)

        task_embedding = torch.zeros([self.batch_size, self.output_dim])
        task_embedding[:, self.target_label] = 1
        task_embedding = task_embedding.to(self.device)

        init_fore_model = copy.deepcopy(self.fore_model)
        for p in init_fore_model.parameters():
            p.requires_grad = False

        self.fore_model.train()
        self.weight_model.train()

View on GitHub (pinned to 79633dd950)

Solutions

  1. Set mode='hard' or mode='soft' in the TCTSModel config — the only supported branches.
  2. Pick 'hard' to mimic selecting the single most-likely horizon step, 'soft' for probability-weighted aggregation across steps.
  3. For a custom weighting scheme, subclass TCTSModel and extend loss_fn with a new branch before the else.

Example fix

# before
model = TCTSModel(..., mode="weighted")

# after
model = TCTSModel(..., mode="soft")
Defensive patterns

Strategy: validation

Validate before calling

assert model_kwargs.get("mode") in ("hard", "soft"), f"TCTSModel mode must be 'hard' or 'soft', got {model_kwargs.get('mode')!r}"

Try / catch

try:
    model.fit(dataset)
except NotImplementedError as e:
    if "mode" in str(e):
        model_kwargs["mode"] = "soft"
        model = TCTSModel(**model_kwargs)
        model.fit(dataset)
    else:
        raise

Prevention

When it happens

Trigger: Constructing TCTSModel (trend-cascade time-series model) with mode='mixed', mode='median', or any string besides 'hard'/'soft', then calling fit(); train_epoch's loss_fn call raises on the first batch.

Common situations: Copying hyperparameters from the TRA paper/baselines where other mode names appear; typo like 'Hard'; assumption that a default exists — check that mode was actually passed, since an unset/None value also fails.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/a764a3c5d3a5f752. Report an issue: GitHub.