Typed Groups
Typed Groups
Tutorial: Typed GroupsSee also: Examples: Typed Variants, API: language
Problem Statement
@L.params lets you parameterize a single config variant. It doesn't enforce
that you've chosen a valid alternative (e.g. a valid optimizer), or catch
typos in alternative names. Typed groups solve both.
L.Group[T]
Define a group by subclassing L.Group[T], where T is the runtime type
all entries produce:
class OptimGroup(L.Group[torch.optim.Optimizer]):
sgd = L.call(torch.optim.SGD)(lr=1e-2, momentum=0.9)
adam = L.call(torch.optim.Adam)(lr=1e-3)
L.Group.__init_subclass__ registers each class attribute as a named entry
in Hydra's ConfigStore under the group name optim.
@L.config
Annotate the schema (the config dataclass) with @L.config:
@L.config
class TrainSchema:
optimizer: torch.optim.Optimizer = L.slot(OptimGroup)
model: nn.Module = L.call(nn.Linear)(784, 10)
lr: float = 1e-3
@L.config applies @dataclass and dataclass_transform so IDE autocomplete
works on TrainSchema instances. Fields annotated with group types are
type-checked: assigning a non-OptimGroup entry raises at config time.
L.slot(G)
L.slot(G) creates a placeholder for a group entry: a _SlotSpec that
resolves at load time to the ${package} OmegaConf interpolation. The field
name is used as the package key by default:
optimizer: OptimGroup = L.slot(OptimGroup) # package = "optimizer"
L.bind and L.Defaults
L.bind(slot, entry) wires a specific entry into a slot at config definition
time:
defaults = L.Defaults(
L.self_,
L.bind(TrainSchema.optimizer, OptimGroup.adam),
)
L.Defaults produces the Hydra defaults list. L.self_ is the _self_
sentinel for Hydra's composition order. L.delete(G) removes a default.
L.bind_strict(slot, L.use(entry)) is the type-safe variant, verified at
schema definition time.
L.chosen(G)
Use L.chosen(G) inside a config body to get a typed reference to whatever
entry was composed into that group slot:
@L.config
class TrainSchema:
optimizer: torch.optim.Optimizer = L.slot(OptimGroup)
# chosen resolves to the actual composed optimizer config
effective_lr: float = L.chosen(OptimGroup).lr
Switching Entries
From Python:
cfg = laco.load("configs://examples/typed/mlp.py?activation=gelu")
From CLI:
laco run configs://examples/typed/mlp.py optimizer=adam
Laco validates that adam is a registered entry in OptimGroup and raises
ValueError if not.