microsoft/qlib · error · NotImplementedError

optimizer {} is not supported!

Error message

optimizer {} is not supported!

What it means

Raised by the SANDWICH model's init in qlib/contrib/model/pytorch_sandwich.py:222 when the optimizer parameter, lowercased, is neither 'adam' nor 'gd'. 'gd' maps to torch.optim.SGD with the configured lr. Any other string (e.g. 'sgd', 'adamw', 'rmsprop') hits the else branch and raises NotImplementedError.

Source

Thrown at qlib/contrib/model/pytorch_sandwich.py:222

        self.sandwich_model = SandwichModel(
            fea_dim=self.fea_dim,
            cnn_dim_1=self.cnn_dim_1,
            cnn_dim_2=self.cnn_dim_2,
            cnn_kernel_size=self.cnn_kernel_size,
            rnn_dim_1=self.rnn_dim_1,
            rnn_dim_2=self.rnn_dim_2,
            rnn_dups=self.rnn_dups,
            rnn_layers=self.rnn_layers,
            dropout=self.dropout,
            device=self.device,
        )
        if optimizer.lower() == "adam":
            self.train_optimizer = optim.Adam(self.sandwich_model.parameters(), lr=self.lr)
        elif optimizer.lower() == "gd":
            self.train_optimizer = optim.SGD(self.sandwich_model.parameters(), lr=self.lr)
        else:
            raise NotImplementedError("optimizer {} is not supported!".format(optimizer))

        self.fitted = False
        self.sandwich_model.to(self.device)

    @property
    def use_gpu(self):
        return self.device != torch.device("cpu")

    def mse(self, pred, label):
        loss = (pred - label) ** 2
        return torch.mean(loss)

    def loss_fn(self, pred, label):
        mask = ~torch.isnan(label)

        if self.loss == "mse":
            return self.mse(pred[mask], label[mask])

View on GitHub (pinned to 79633dd950)

Solutions

  1. Set optimizer: 'adam' or 'gd' (gd = plain SGD at the given lr) in the model kwargs.
  2. If you truly need AdamW/RMSProp, subclass and construct the optimizer yourself after super().__init__ with a supported placeholder value.

Example fix

# before
kwargs:
  optimizer: sgd

# after
kwargs:
  optimizer: gd   # plain SGD; or 'adam'
Defensive patterns

Strategy: validation

Validate before calling

optimizer = config["optimizer"]
assert optimizer.lower() in ("adam", "gd"), f"optimizer must be 'adam' or 'gd' (plain SGD), got {optimizer!r}"

Type guard

def is_supported_optimizer(optimizer: str) -> bool:
    return optimizer.lower() in ("adam", "gd")

Try / catch

try:
    model = SandwichModel(**kwargs)
except NotImplementedError as e:
    if "optimizer" in str(e):
        raise ValueError("Use optimizer='adam' or 'gd'; 'sgd' is not a valid token") from e
    raise

Prevention

When it happens

Trigger: Instantiating the sandwich model with optimizer='sgd' (the correct token is 'gd'), 'adamw', 'rmsprop', or any unsupported name; the raise happens during model construction, before fit().

Common situations: Muscle-memory 'sgd' from other frameworks — qlib's token is 'gd'; using optimizer settings copied from LightGBM/XGBoost model sections in the same workflow file.

Related errors


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