microsoft/qlib · error · NotImplementedError

optimizer {} is not supported!

Error message

optimizer {} is not supported!

What it means

Raised in GRUModel.__init__ when the `optimizer` hyper-parameter is not "adam" or "gd" (case-insensitive). Only Adam and plain SGD are wired up for the GRU model; the check happens at construction time, so a bad value fails fast before any training.

Source

Thrown at qlib/contrib/model/pytorch_gru.py:127

        if self.seed is not None:
            np.random.seed(self.seed)
            torch.manual_seed(self.seed)

        self.gru_model = GRUModel(
            d_feat=self.d_feat,
            hidden_size=self.hidden_size,
            num_layers=self.num_layers,
            dropout=self.dropout,
        )
        self.logger.info("model:\n{:}".format(self.gru_model))
        self.logger.info("model size: {:.4f} MB".format(count_parameters(self.gru_model)))

        if optimizer.lower() == "adam":
            self.train_optimizer = optim.Adam(self.gru_model.parameters(), lr=self.lr)
        elif optimizer.lower() == "gd":
            self.train_optimizer = optim.SGD(self.gru_model.parameters(), lr=self.lr)
        else:
            raise NotImplementedError("optimizer {} is not supported!".format(optimizer))

        self.fitted = False
        self.gru_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. Use optimizer="adam" or optimizer="gd" (the latter maps to torch.optim.SGD without momentum).
  2. For a custom optimizer, subclass GRUModel and set self.train_optimizer yourself after calling super().__init__ with a supported placeholder value.
  3. If you need momentum/weight decay, prefer subclassing with optim.SGD(momentum=...) rather than a new name.

Example fix

# before
GRUModel(optimizer="sgd", ...)  # NotImplementedError

# after
GRUModel(optimizer="gd", ...)   # plain SGD
Defensive patterns

Strategy: validation

Validate before calling

assert params["optimizer"].lower() in {"adam", "gd"}, "GRUModel supports only 'adam' and 'gd'"

Type guard

def is_supported_optimizer(name: str) -> bool:
    return isinstance(name, str) and name.lower() in {"adam", "gd"}

Try / catch

try:
    model = GRUModel(**params)
except NotImplementedError as e:
    if "optimizer" in str(e):
        params["optimizer"] = "adam"
        model = GRUModel(**params)
    else:
        raise

Prevention

When it happens

Trigger: Constructing GRUModel(optimizer="sgd", ...) — note the accepted alias for SGD is "gd", not "sgd" — or optimizer="adamw"/"rmsprop"/"adagrad". Lower-cased comparison means "Adam" works but "adam_w" does not.

Common situations: Using the conventional name "sgd" instead of qlib's "gd"; copying optimizer names from other frameworks or other qlib contrib models that support more optimizers; typos like "adm".

Related errors


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