{"record":{"id":"37767fc1e0918a1b","repo":"microsoft/qlib","slug":"optimizer-is-not-supported-37767f","errorCode":null,"errorMessage":"optimizer {} is not supported!","messagePattern":"optimizer (.+?) is not supported!","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/model/pytorch_gats.py","lineNumber":135,"sourceCode":"            np.random.seed(self.seed)\n            torch.manual_seed(self.seed)\n\n        self.GAT_model = GATModel(\n            d_feat=self.d_feat,\n            hidden_size=self.hidden_size,\n            num_layers=self.num_layers,\n            dropout=self.dropout,\n            base_model=self.base_model,\n        )\n        self.logger.info(\"model:\\n{:}\".format(self.GAT_model))\n        self.logger.info(\"model size: {:.4f} MB\".format(count_parameters(self.GAT_model)))\n\n        if optimizer.lower() == \"adam\":\n            self.train_optimizer = optim.Adam(self.GAT_model.parameters(), lr=self.lr)\n        elif optimizer.lower() == \"gd\":\n            self.train_optimizer = optim.SGD(self.GAT_model.parameters(), lr=self.lr)\n        else:\n            raise NotImplementedError(\"optimizer {} is not supported!\".format(optimizer))\n\n        self.fitted = False\n        self.GAT_model.to(self.device)\n\n    @property\n    def use_gpu(self):\n        return self.device != torch.device(\"cpu\")\n\n    def mse(self, pred, label):\n        loss = (pred - label) ** 2\n        return torch.mean(loss)\n\n    def loss_fn(self, pred, label):\n        mask = ~torch.isnan(label)\n\n        if self.loss == \"mse\":\n            return self.mse(pred[mask], label[mask])\n","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/pytorch_gats.py#L117-L153","documentation":"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.","triggerScenarios":"Calling GATsModel(...).fit(dataset) with optimizer set to 'sgd' (note: the code expects 'gd' for SGD), 'adagrad', 'rmsprop', or 'adamw'.","commonSituations":"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.","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."],"exampleFix":"# before\nmodel = GATsModel(optimizer='sgd')\n\n# after\nmodel = GATsModel(optimizer='gd')","handlingStrategy":"validation","validationCode":"assert optimizer.lower() in ('adam', 'gd'), f\"optimizer must be 'adam' or 'gd', got {optimizer!r}\"","typeGuard":"def is_supported_optimizer(name: str) -> bool:\n    return name.lower() in ('adam', 'gd')","tryCatchPattern":"try:\n    model.fit(dataset)\nexcept NotImplementedError as e:\n    if 'optimizer' in str(e):\n        # fall back to the default Adam and retry\n        model = GATsModel(optimizer='adam')\n        model.fit(dataset)\n    else:\n        raise","preventionTips":["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."],"tags":["pytorch","qlib","optimizer","config-validation","gats"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}