microsoft/qlib · error · NotImplementedError
optimizer {} is not supported!
Error message
optimizer {} is not supported! What it means
GATsModel.fit() constructs the optimizer from the optimizer hyperparameter with only two branches: 'adam' (case-insensitive) maps to torch.optim.Adam and 'gd' maps to torch.optim.SGD. Any other string raises NotImplementedError. This happens during fit() before any training begins, so the model is cheap to recover from: fix the string and call fit() again.
Source
Thrown at qlib/contrib/model/pytorch_gats.py:135
np.random.seed(self.seed)
torch.manual_seed(self.seed)
self.GAT_model = GATModel(
d_feat=self.d_feat,
hidden_size=self.hidden_size,
num_layers=self.num_layers,
dropout=self.dropout,
base_model=self.base_model,
)
self.logger.info("model:\n{:}".format(self.GAT_model))
self.logger.info("model size: {:.4f} MB".format(count_parameters(self.GAT_model)))
if optimizer.lower() == "adam":
self.train_optimizer = optim.Adam(self.GAT_model.parameters(), lr=self.lr)
elif optimizer.lower() == "gd":
self.train_optimizer = optim.SGD(self.GAT_model.parameters(), lr=self.lr)
else:
raise NotImplementedError("optimizer {} is not supported!".format(optimizer))
self.fitted = False
self.GAT_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
- Use 'adam' or 'gd' (the model's name for plain SGD), matched case-insensitively.
- If you wrote 'sgd', change it to 'gd'.
- For a different optimizer, subclass GATsModel and add a branch constructing the torch.optim class you need.
Example fix
# before model = GATsModel(optimizer='sgd') # after model = GATsModel(optimizer='gd')
Defensive patterns
Strategy: validation
Validate before calling
assert optimizer.lower() in ('adam', 'gd'), f"optimizer must be 'adam' or 'gd', got {optimizer!r}" Type guard
def is_supported_optimizer(name: str) -> bool:
return name.lower() in ('adam', 'gd') Try / catch
try:
model.fit(dataset)
except NotImplementedError as e:
if 'optimizer' in str(e):
# fall back to the default Adam and retry
model = GATsModel(optimizer='adam')
model.fit(dataset)
else:
raise Prevention
- Remember 'gd' (not 'sgd') is this family's name for SGD.
- Validate optimizer names against the fixed allowlist before training.
- Encode the allowlist in your workflow config schema to catch typos early.
When it happens
Trigger: Calling GATsModel(...).fit(dataset) with optimizer set to 'sgd' (note: the code expects 'gd' for SGD), 'adagrad', 'rmsprop', or 'adamw'.
Common situations: The 'sgd' vs 'gd' naming trap is the most common hit: developers naturally write 'sgd' and get this error; copying optimizer names from other qlib models or sklearn-style configs; wanting AdamW for weight decay and finding it unsupported.
Related errors
- unknown loss `%s`
- unknown metric `%s`
- unknown base model name `%s`
- optimizer {} is not supported!
- optimizer {} is not supported!
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/37767fc1e0918a1b.
Report an issue: GitHub.