Skip to content

RFC: Sub-config overhaul #66

@gasteigerjo

Description

@gasteigerjo

Overview

This RFC proposes to change how we handle sub-configs in order to make them more flexible. Instead of treating a sub-config as a sub-experiment that is just run as is, this proposal is about using them as definitions that can then be flexibly used in the remaining config.

Example

# Define sub-configs to use them later
nitrogen:
  ...
cyclobutadiene:
  ...
short_training:
  ...
long_training:
  ...
model1:
  ...
model2:
  ...

# The user has to specify how to use the sub-configs
load:
  - model1  # directly specifying a sub-config means always using it (like `fixed`)
  - choice:
    - nitrogen
    - cyclobutadiene
  - choice:
    - short_training
    - long_training

grid:
  other_attribute:
    type: choice
    options:
      - True
      - False

Considerations

We currently assume that always exactly one sub-config is chosen. This proposal allows users to define themselves how they want to combine sub-configs. Specifying them all in a grid would replicate the behavior we had previously, so this is a natural extension.

A sub-config can itself contain fixed/grid/random blocks and further sub-configs, just like they do now. The outer set of configs is then combined with the inner set of configs (outer product), just like now. I.e. loading a single sub-config can result in multiple configs, if the sub-config contains a grid.

Including separate files

This also naturally extends to loading separate config files into one config. The other config file can be treated exactly the same way as a subconfig. The user would only have to provide a keyword, specifying that this is now an external file. I.e.

# Define sub-configs
nitrogen:
  ...
model2:
  ...

load:
  - model2
  - file: ~/test/general.yaml  # A full config file, with fixed, grid, and random blocks.
  - choice:
    - nitrogen
    - simple_file: ~/test/dataset2.yaml  # A simple config file, which only containes fixed parameter values, similar to what Sacred uses.

Possible variant

While defining sub-configs seems pretty clear (that's exactly how we do it currently), specifying their usage still remains an open question. Instead of creating a new top-level block load, we could also integrate loading sub-configs into the fixed/grid/random blocks by introducing a special load keyword. However, this is much more difficult, considering the restrictions of yaml (every key has to be unique).

fixed:
  model1: load  # Always use everything from one sub-config

grid:
  dataset:  # The name doesn't do anything, but it has to be unique (due to yaml)
    type: load  # Specifies that we want to load sub-configs
    options:
      - nitrogen
      - cyclobutadiene
      - file: ~/test/general.yaml
      - simple_file: ~/test/dataset2.yaml
  training:
    type: load
    options:
      - short_training  # Multiple sub-configs can be combined
      - long_training

I prefer the original suggestion. It also seems to align better with user expectations. With this variant, loading a sub-config in the fixed block could lead to multiple different configs, which might be unexpected.

Open questions

There are 2 open questions:

  • What is the best way of specifying load for fixed/grid/random? How do we best specify importing files in these 3 cases? -> No random; use keywords fixed and choice.
  • How to best handle conflicting parameters? We could (1) always raise an error if there are conflicting parameters in separate sub-configs or the config vs. the sub-config. Or (2) we handle them like we currently do, by treating sub-configs as a hierarchy and using the deepest parameter definition, if there is a purely descending path between them (but show a warning). Otherwise, we raise an error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions