diff --git a/configs/README.md b/configs/README.md deleted file mode 100644 index 6d8772f4ce9e76b011ffcf38e25d3924359e3a37..0000000000000000000000000000000000000000 --- a/configs/README.md +++ /dev/null @@ -1,103 +0,0 @@ -Habitat-API Configuration -============================== - -Habitat-API uses [Yacs configuration system](https://github.com/rbgirshick/yacs) -with the paradigm of `your code + a YACS config for experiment E (+ -external dependencies + hardware + other nuisance terms ...) = -reproducible experiment E`. Yacs advantages: -- Checks for type consistency. -- All parameters and default values are searchable in the code. -- A parameter doesn't need to be set always as each parameter has a - default value. -- Ability to freeze config to prevent unintended changes. - -## Config usage -An example of how to merge default config with 2 others configs and overwrite -one parameter that could come from the command line: -``` - merged_config = get_config( - config_paths=["configs/tasks/pointnav.yaml", - "configs/dataset/val.yaml"], - opts=["ENVIRONMENT.MAX_EPISODE_STEPS", steps_limit] - ) - -``` - -## Config structure -Below is the structure of config used for Habitat: -- Environment -- Task - - Sensors - - Measurements -- Simulator - - Agent - - Sensors -- Dataset - -We use node names (e.g. `SENSORS: ['RGB_SENSOR', 'DEPTH_SENSOR']`) instead of list -of config nodes (e.g. `SENSORS: [{TYPE = "HabitatSimDepthSensor", -MIN_DEPTH = 0}, ...]`) to declare the Sensors attached to an Agent or Measures -enabled for the Task . With this approach, it's still easy to overwrite a -particular sensor parameter in yaml file without redefining the whole sensor -config. - -## Extending the config -Example of how to extend a config outside of `habtiat-api` repository. -First, we create a config extending the default config in the code and re-use -`habitat.get_config()`: -``` -import habitat -import argparse -from typing import List, Optional, Union - -_C = habitat.get_config() -_C.defrost() -# Add new parameters to the config -_C.TASK.EPISODE_INFO = habitat.Config() -_C.TASK.EPISODE_INFO.TYPE = "EpisodeInfo" -_C.TASK.EPISODE_INFO.VALUE = 5 -_C.TASK.MEASUREMENTS.append("EPISODE_INFO") - -# New function returning extended Habitat config that should be used instead -# of habitat.get_config() -def my_get_config( - config_paths: Optional[Union[List[str], str]] = None, - opts: Optional[list] = None, -) -> habitat.Config: - CONFIG_FILE_SEPARATOR = "," - config = _C.clone() - if config_paths: - if isinstance(config_paths, str): - if CONFIG_FILE_SEPARATOR in config_paths: - config_paths = config_paths.split(CONFIG_FILE_SEPARATOR) - else: - config_paths = [config_paths] - - for config_path in config_paths: - config.merge_from_file(config_path) - - if opts: - config.merge_from_list(opts) - - config.freeze() - return config - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument( - "--task-config", - type=str, - default="configs/tasks/pointnav.yaml," - "configs/datasets/pointnav/habitat_test.yaml", - ) - parser.add_argument( - "opts", - default=None, - nargs=argparse.REMAINDER, - help="Modify config options from command line", - ) - args = parser.parse_args() - config = my_get_config(config_paths=args.task_config, opts=args.opts) - env = habitat.Env(config) - -``` diff --git a/configs/test/new_keys_test.yaml b/configs/test/new_keys_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..af9cbb87e778b2959cd890a34ffda218ba58584d --- /dev/null +++ b/configs/test/new_keys_test.yaml @@ -0,0 +1,6 @@ +ENVIRONMENT: + NEW_KEY: 20 + ITERATOR_OPTIONS: + MY_PARAM: "test" +TASK: + MY_NEW_TASK_PARAM: test diff --git a/habitat/config/__init__.py b/habitat/config/__init__.py index 6734e3fc2cfeb467fbe56f92f09e5d9c9b7e6d8a..408bf75753299706465cda59b00dce7514f83f94 100644 --- a/habitat/config/__init__.py +++ b/habitat/config/__init__.py @@ -3,9 +3,122 @@ # Copyright (c) Facebook, Inc. and its affiliates. # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +from habitat.config.default import Config, get_config -from yacs.config import CfgNode as Config +r"""Habitat-API Configuration +============================== -from habitat.config.default import get_config +Habitat-API uses [Yacs configuration system](https://github.com/rbgirshick/yacs) +with the paradigm of `your code + a YACS config for experiment E (+ +external dependencies + hardware + other nuisance terms ...) = +reproducible experiment E`. Yacs advantages: +- Checks for type consistency. +- All parameters and default values are searchable in the code. +- A parameter doesn't need to be set always as each parameter can have a + default value. +- Ability to freeze config to prevent unintended changes. + +## Config usage +An example of how to merge default config with 2 others configs and overwrite +one parameter that could come from the command line: +``` + merged_config = get_config( + config_paths=["configs/tasks/pointnav.yaml", + "configs/dataset/val.yaml"], + opts=["ENVIRONMENT.MAX_EPISODE_STEPS", steps_limit] + ) + +``` + +## Config structure +Below is the structure of config used for Habitat: +- Environment +- Task + - Sensors + - Measurements +- Simulator + - Agent + - Sensors +- Dataset + +We use node names (e.g. `SENSORS: ['RGB_SENSOR', 'DEPTH_SENSOR']`) instead of list +of config nodes (e.g. `SENSORS: [{TYPE = "HabitatSimDepthSensor", +MIN_DEPTH = 0}, ...]`) to declare the Sensors attached to an Agent or Measures +enabled for the Task . With this approach, it's still easy to overwrite a +particular sensor parameter in yaml file without redefining the whole sensor +config. + +## Extending the config without defaults +Create a YAML file and add new fields and values. Load the custom config using +`habitat.get_config()` and defined fields will be merged in default Habitat config: +``` +import habitat +import argparse +from typing import List, Optional, Union + +config = habitat.get_config("{path to user define yaml config}") +env = habitat.Env(config) +``` + +## Extending the config with default values +Example of how to extend a config outside of `habtiat-api` repository. +First, we create a config extending the default config in the code and re-use +`habitat.get_config()`: +``` +import habitat +import argparse +from typing import List, Optional, Union + +_C = habitat.get_config() +_C.defrost() +# Add new parameters to the config +_C.TASK.EPISODE_INFO = habitat.Config() +_C.TASK.EPISODE_INFO.TYPE = "EpisodeInfo" +_C.TASK.EPISODE_INFO.VALUE = 5 +_C.TASK.MEASUREMENTS.append("EPISODE_INFO") + +# New function returning extended Habitat config that should be used instead +# of habitat.get_config() +def my_get_config( + config_paths: Optional[Union[List[str], str]] = None, + opts: Optional[list] = None, +) -> habitat.Config: + CONFIG_FILE_SEPARATOR = "," + config = _C.clone() + if config_paths: + if isinstance(config_paths, str): + if CONFIG_FILE_SEPARATOR in config_paths: + config_paths = config_paths.split(CONFIG_FILE_SEPARATOR) + else: + config_paths = [config_paths] + + for config_path in config_paths: + config.merge_from_file(config_path) + + if opts: + config.merge_from_list(opts) + + config.freeze() + return config + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--task-config", + type=str, + default="configs/tasks/pointnav.yaml," + "configs/datasets/pointnav/habitat_test.yaml", + ) + parser.add_argument( + "opts", + default=None, + nargs=argparse.REMAINDER, + help="Modify config options from command line", + ) + args = parser.parse_args() + config = my_get_config(config_paths=args.task_config, opts=args.opts) + env = habitat.Env(config) + +```""" __all__ = ["Config", "get_config"] diff --git a/habitat/config/default.py b/habitat/config/default.py index a57dd16de46d46b87cebb478cd783c3b78adb762..559e2c46670e8942da9ce92719eb47279b8a3eef 100644 --- a/habitat/config/default.py +++ b/habitat/config/default.py @@ -6,7 +6,17 @@ from typing import List, Optional, Union -from habitat.config import Config as CN # type: ignore +import yacs.config + +# from habitat.config import Config as CN # type: ignore + +# Default Habitat config node +class Config(yacs.config.CfgNode): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs, new_allowed=True) + + +CN = Config DEFAULT_CONFIG_DIR = "configs/" CONFIG_FILE_SEPARATOR = "," diff --git a/test/test_config.py b/test/test_config.py index a113e82e91354f6d501c103db46bfdcb73edb91f..d9d5b8a8490434c7640d34ba8613f8d97594207e 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -8,6 +8,7 @@ from habitat.config.default import get_config CFG_TEST = "configs/test/habitat_all_sensors_test.yaml" CFG_EQA = "configs/test/habitat_mp3d_eqa_test.yaml" +CFG_NEW_KEYS = "configs/test/new_keys_test.yaml" MAX_TEST_STEPS_LIMIT = 3 @@ -22,6 +23,20 @@ def test_merged_configs(): ) +def test_new_keys_merged_configs(): + test_config = get_config(CFG_TEST) + new_keys_config = get_config(CFG_NEW_KEYS) + merged_config = get_config("{},{}".format(CFG_TEST, CFG_NEW_KEYS)) + assert ( + merged_config.TASK.MY_NEW_TASK_PARAM + == new_keys_config.TASK.MY_NEW_TASK_PARAM + ) + assert ( + merged_config.ENVIRONMENT.MAX_EPISODE_STEPS + == test_config.ENVIRONMENT.MAX_EPISODE_STEPS + ) + + def test_overwrite_options(): for steps_limit in range(MAX_TEST_STEPS_LIMIT): config = get_config(