From b2126277a124604d73eddd90e1f8adb6234fcad4 Mon Sep 17 00:00:00 2001 From: Madeline Scyphers Date: Fri, 18 Aug 2023 16:30:11 -0400 Subject: [PATCH 1/3] Add jinja2 support for configs Jinja2 working for configs, parsed on config load working example of parameters and constraints from for loop in tests --- boa/template.py | 12 +++++- boa/wrappers/wrapper_utils.py | 22 +++++------ .../1unit_tests/test_config_instantiation.py | 6 +++ tests/test_configs/test_config_jinja2.yaml | 37 +++++++++++++++++++ 4 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 tests/test_configs/test_config_jinja2.yaml diff --git a/boa/template.py b/boa/template.py index d1fd63e..afd30aa 100644 --- a/boa/template.py +++ b/boa/template.py @@ -1,6 +1,14 @@ +from __future__ import annotations + +from typing import Optional + import jinja2 -def render_template(template_name, **kwargs): - template = jinja2.Environment(loader=jinja2.FileSystemLoader("templates")).get_template(template_name) +def render_template_from_path(path, template_kw: Optional[dict] = None, **kwargs): + if template_kw is None: + template_kw = {} + template_kw["extensions"].append("jinja2.ext.do") + with open(path) as f: + template = jinja2.Template(f.read(), **template_kw) return template.render(**kwargs) diff --git a/boa/wrappers/wrapper_utils.py b/boa/wrappers/wrapper_utils.py index d01809f..7cc35c6 100644 --- a/boa/wrappers/wrapper_utils.py +++ b/boa/wrappers/wrapper_utils.py @@ -16,7 +16,7 @@ import shlex from contextlib import contextmanager from functools import wraps -from typing import TYPE_CHECKING, Type +from typing import TYPE_CHECKING, Optional, Type import ruamel.yaml as yaml from ax.core.base_trial import BaseTrial @@ -27,6 +27,7 @@ from boa.definitions import IS_WINDOWS, PathLike, PathLike_tup from boa.logger import get_logger +from boa.template import render_template_from_path from boa.utils import ( _load_attr_from_module, _load_module_from_path, @@ -175,7 +176,7 @@ def split_shell_command(cmd: str): return shlex.split(cmd, posix=not IS_WINDOWS) -def load_json(file: PathLike) -> dict: +def load_json(file: PathLike, template_kw: Optional[dict] = None) -> dict: """ Read experiment configuration file for setting up the optimization. The configuration file contains the list of parameters, and whether each parameter is a fixed @@ -208,27 +209,26 @@ def load_json(file: PathLike) -> dict: :func:`.normalize_config` for information on ``parameter_keys`` option """ file = pathlib.Path(file).expanduser() - with open(file, "r") as f: - config = json.load(f) - + s = render_template_from_path(file, template_kw=template_kw) + config = json.loads(s) return config @copy_doc(load_json) -def load_yaml(file: PathLike) -> dict: +def load_yaml(file: PathLike, template_kw: Optional[dict] = None) -> dict: file = pathlib.Path(file).expanduser() - with open(file, "r") as f: - config: dict = yaml.safe_load(f) + s = render_template_from_path(file, template_kw=template_kw) + config: dict = yaml.safe_load(s) return config @copy_doc(load_json) -def load_jsonlike(file: PathLike): +def load_jsonlike(file: PathLike, template_kw: Optional[dict] = None) -> dict: file = pathlib.Path(file) if file.suffix.lstrip(".").lower() in {"yaml", "yml"}: - return load_yaml(file) + return load_yaml(file, template_kw=template_kw) elif file.suffix.lstrip(".").lower() == "json": - return load_json(file) + return load_json(file, template_kw=template_kw) else: raise ValueError(f"Invalid config file format for config file {file}\nAccepted file formats are YAML and JSON.") diff --git a/tests/1unit_tests/test_config_instantiation.py b/tests/1unit_tests/test_config_instantiation.py index e4378e9..239149f 100644 --- a/tests/1unit_tests/test_config_instantiation.py +++ b/tests/1unit_tests/test_config_instantiation.py @@ -4,3 +4,9 @@ def test_config_instantiation(): BOAConfig.from_jsonlike(TEST_CONFIG_DIR / "test_config_generic.yaml") + + +def test_config_with_jinja2(): + config = BOAConfig.from_jsonlike(TEST_CONFIG_DIR / "test_config_jinja2.yaml") + assert len(config.parameters) == 13 + assert len(config.parameter_constraints) == 13 diff --git a/tests/test_configs/test_config_jinja2.yaml b/tests/test_configs/test_config_jinja2.yaml new file mode 100644 index 0000000..85e5472 --- /dev/null +++ b/tests/test_configs/test_config_jinja2.yaml @@ -0,0 +1,37 @@ +# We can set up a list of parameter names to be used throughout the config file +{% set param_names = [] %} +{% for i in range(10) %} + {% do param_names.append("x" + i|string) %} +{% endfor %} +# We could also do this directly instead of in a list +{% set param_names2 = ["y0", "y1", "y2"] %} +objective: + metrics: + # List all of your metrics here, + # only list 1 metric for a single objective optimization + - name: rmse + metric: RootMeanSquaredError + +parameters: + {% for param in param_names %} + {{ param }}: + type: range + bounds: [0, 1] + value_type: float + {% endfor %} + {% for param in param_names2 %} + {{ param }}: + type: range + bounds: [0.0, 1.0] + {% endfor %} + +parameter_constraints: + {% for param in param_names %} + - {{ param }} <= .5 + {% endfor %} + {% for param in param_names2 %} + - {{ param }} >= .5 + {% endfor %} + +scheduler: + n_trials: 10 From 1ec6fa61e7bb0e4ba1427e7a2f78ac2e4580b1c7 Mon Sep 17 00:00:00 2001 From: Madeline Scyphers Date: Fri, 18 Aug 2023 18:00:48 -0400 Subject: [PATCH 2/3] Minor edits to allow more easy access to templating api if using python --- boa/__init__.py | 1 + boa/__main__.py | 1 - boa/config/config.py | 4 +-- boa/template.py | 14 +++++++++- boa/wrappers/wrapper_utils.py | 28 ++++++++----------- .../1unit_tests/test_config_instantiation.py | 6 ---- tests/1unit_tests/test_jinja.py | 8 ++++++ tests/test_configs/test_config_jinja2.yaml | 2 +- 8 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 tests/1unit_tests/test_jinja.py diff --git a/boa/__init__.py b/boa/__init__.py index 0391ed7..d9d1a50 100644 --- a/boa/__init__.py +++ b/boa/__init__.py @@ -28,6 +28,7 @@ from boa.runner import * # noqa from boa.scheduler import * # noqa from boa.storage import * # noqa +from boa.template import render_template_from_path # noqa from boa.wrappers.base_wrapper import * # noqa from boa.wrappers.script_wrapper import * # noqa from boa.wrappers.wrapper_utils import * # noqa diff --git a/boa/__main__.py b/boa/__main__.py index 0ce3da7..76817ec 100644 --- a/boa/__main__.py +++ b/boa/__main__.py @@ -48,7 +48,6 @@ ) @click.option( "--rel-to-config/--rel-to-here", # more cli friendly name for config option of rel_to_launch - # default=fields_dict(BOAConfig)["rel_to_config"].default, # make default opposite of rel_to_config default=None, help="Define all path and dir options in your config file relative to where boa is launched from" " instead of relative to the config file location (the default)" diff --git a/boa/config/config.py b/boa/config/config.py index ccf26ff..3282376 100644 --- a/boa/config/config.py +++ b/boa/config/config.py @@ -583,9 +583,9 @@ def __init__(self, parameter_keys=None, **config): self.__attrs_init__(**config, parameter_keys=parameter_keys) @classmethod - def from_jsonlike(cls, file, rel_to_config: Optional[bool] = None): + def from_jsonlike(cls, file, rel_to_config: Optional[bool] = None, template_kw: Optional[dict] = None): config_path = pathlib.Path(file).resolve() - config = load_jsonlike(config_path) + config = load_jsonlike(config_path, template_kw=template_kw) config = cls.convert_deprecated(configd=config) diff --git a/boa/template.py b/boa/template.py index afd30aa..e085f24 100644 --- a/boa/template.py +++ b/boa/template.py @@ -6,9 +6,21 @@ def render_template_from_path(path, template_kw: Optional[dict] = None, **kwargs): + """ + Render a template from a path. + + Parameters + ---------- + path : str + Path to the template file. + template_kw : dict, optional + Dictionary of keyword arguments to pass to the jinja2.Template constructor. + **kwargs + Keyword arguments to pass to the template as variables to render. + """ if template_kw is None: template_kw = {} - template_kw["extensions"].append("jinja2.ext.do") + template_kw.setdefault("extensions", []).append("jinja2.ext.do") with open(path) as f: template = jinja2.Template(f.read(), **template_kw) return template.render(**kwargs) diff --git a/boa/wrappers/wrapper_utils.py b/boa/wrappers/wrapper_utils.py index 7cc35c6..dd417fe 100644 --- a/boa/wrappers/wrapper_utils.py +++ b/boa/wrappers/wrapper_utils.py @@ -16,7 +16,7 @@ import shlex from contextlib import contextmanager from functools import wraps -from typing import TYPE_CHECKING, Optional, Type +from typing import TYPE_CHECKING, Type import ruamel.yaml as yaml from ax.core.base_trial import BaseTrial @@ -176,7 +176,7 @@ def split_shell_command(cmd: str): return shlex.split(cmd, posix=not IS_WINDOWS) -def load_json(file: PathLike, template_kw: Optional[dict] = None) -> dict: +def load_json(file: PathLike, **kwargs) -> dict: """ Read experiment configuration file for setting up the optimization. The configuration file contains the list of parameters, and whether each parameter is a fixed @@ -187,12 +187,9 @@ def load_json(file: PathLike, template_kw: Optional[dict] = None) -> dict: ---------- file File path for the experiment configuration file - normalize - Whether to run :func:`.normalize_config` after loading config - to run certain predictable configuration normalization. (default true) - parameter_keys - Alternative keys or paths to keys to parse as parameters to optimize, - for more information, see :func:`.wpr_params_to_boa` + kwargs + variables to pass to :func:`boa.template.render_template_from_path` + for rendering in your loaded file Examples -------- @@ -204,31 +201,28 @@ def load_json(file: PathLike, template_kw: Optional[dict] = None) -> dict: ------- loaded_configs: dict - See Also - -------- jmn nmn - :func:`.normalize_config` for information on ``parameter_keys`` option """ file = pathlib.Path(file).expanduser() - s = render_template_from_path(file, template_kw=template_kw) + s = render_template_from_path(file, **kwargs) config = json.loads(s) return config @copy_doc(load_json) -def load_yaml(file: PathLike, template_kw: Optional[dict] = None) -> dict: +def load_yaml(file: PathLike, **kwargs) -> dict: file = pathlib.Path(file).expanduser() - s = render_template_from_path(file, template_kw=template_kw) + s = render_template_from_path(file, **kwargs) config: dict = yaml.safe_load(s) return config @copy_doc(load_json) -def load_jsonlike(file: PathLike, template_kw: Optional[dict] = None) -> dict: +def load_jsonlike(file: PathLike, **kwargs) -> dict: file = pathlib.Path(file) if file.suffix.lstrip(".").lower() in {"yaml", "yml"}: - return load_yaml(file, template_kw=template_kw) + return load_yaml(file, **kwargs) elif file.suffix.lstrip(".").lower() == "json": - return load_json(file, template_kw=template_kw) + return load_json(file, **kwargs) else: raise ValueError(f"Invalid config file format for config file {file}\nAccepted file formats are YAML and JSON.") diff --git a/tests/1unit_tests/test_config_instantiation.py b/tests/1unit_tests/test_config_instantiation.py index 239149f..e4378e9 100644 --- a/tests/1unit_tests/test_config_instantiation.py +++ b/tests/1unit_tests/test_config_instantiation.py @@ -4,9 +4,3 @@ def test_config_instantiation(): BOAConfig.from_jsonlike(TEST_CONFIG_DIR / "test_config_generic.yaml") - - -def test_config_with_jinja2(): - config = BOAConfig.from_jsonlike(TEST_CONFIG_DIR / "test_config_jinja2.yaml") - assert len(config.parameters) == 13 - assert len(config.parameter_constraints) == 13 diff --git a/tests/1unit_tests/test_jinja.py b/tests/1unit_tests/test_jinja.py new file mode 100644 index 0000000..abe3864 --- /dev/null +++ b/tests/1unit_tests/test_jinja.py @@ -0,0 +1,8 @@ +from boa import BOAConfig +from tests.conftest import TEST_CONFIG_DIR + + +def test_config_with_jinja2(): + config = BOAConfig.from_jsonlike(TEST_CONFIG_DIR / "test_config_jinja2.yaml") + assert len(config.parameters) == 13 + assert len(config.parameter_constraints) == 13 diff --git a/tests/test_configs/test_config_jinja2.yaml b/tests/test_configs/test_config_jinja2.yaml index 85e5472..debba44 100644 --- a/tests/test_configs/test_config_jinja2.yaml +++ b/tests/test_configs/test_config_jinja2.yaml @@ -34,4 +34,4 @@ parameter_constraints: {% endfor %} scheduler: - n_trials: 10 + n_trials: 1 From f4dd528293d9e1dbd49ec2a338e1b3397b464ec0 Mon Sep 17 00:00:00 2001 From: Madeline Scyphers Date: Fri, 18 Aug 2023 18:52:16 -0400 Subject: [PATCH 3/3] Doc updates to include jinja and fix sphinx errors --- boa/config/__init__.py | 18 +++++-- boa/metrics/__init__.py | 2 +- boa/metrics/metrics.py | 50 +++++++++---------- boa/plot.py | 10 +++- boa/plotting.py | 15 +++++- boa/wrappers/base_wrapper.py | 2 +- docs/code_reference.rst | 2 + docs/conf.py | 4 +- docs/examples/1run_r_streamlined.ipynb | 2 +- docs/examples/default_config.yaml | 9 ++-- docs/user_guide/configuration.rst | 24 --------- docs/user_guide/package_overview.rst | 30 ++++++----- docs/user_guide/run_script.rst | 10 ++-- .../1unit_tests/test_config_normalization.py | 2 - tests/test_configs/test_config_jinja2.yaml | 7 ++- 15 files changed, 100 insertions(+), 87 deletions(-) delete mode 100644 docs/user_guide/configuration.rst diff --git a/boa/config/__init__.py b/boa/config/__init__.py index 5fbff88..fc7254d 100644 --- a/boa/config/__init__.py +++ b/boa/config/__init__.py @@ -4,13 +4,11 @@ Configuration Guide ################### + *********************** Example Configurations *********************** -This configuration can be generated from BOA with the following command:: - - python -m boa.config --output-path [path to output] Default Configuration ============================== @@ -18,6 +16,20 @@ .. literalinclude:: ../../docs/examples/default_config.yaml :language: YAML + +Jinja2 Templating +============================== + +BOA supports Jinja2 templating in the configuration file. This allows for +the use of variables and conditionals in the configuration file. For example, +the following configuration file uses Jinja2 templating to set the +``parameters`` and ``parameter_constraints`` fields based on a loop. +Much more complex templating is possible, including the use of conditionals. +See :std:doc:`Jinja2 ` for more information on Jinja2 templating +and additional options and examples. + +.. literalinclude:: ../../tests/test_configs/test_config_jinja2.yaml + """ from boa.config.config import * # noqa: F401, F403 diff --git a/boa/metrics/__init__.py b/boa/metrics/__init__.py index 2a12bd0..7614800 100644 --- a/boa/metrics/__init__.py +++ b/boa/metrics/__init__.py @@ -10,5 +10,5 @@ Metrics can be specified in your configuration file by passing in the name of one of the predefined :mod:`Metrics ` in BOA. -See :doc:`/user_guide/configuration` for details. +See :doc:`/api/boa.config` for details. """ diff --git a/boa/metrics/metrics.py b/boa/metrics/metrics.py index a14b289..a258cfd 100644 --- a/boa/metrics/metrics.py +++ b/boa/metrics/metrics.py @@ -19,23 +19,21 @@ .. code-block:: YAML # Single objective optimization config - optimization_options: - objective_options: - objectives: - # List all of your metrics here, - # only list 1 metric for a single objective optimization - - metric: RootMeanSquaredError + objective: + metrics: + # List all of your metrics here, + # only list 1 metric for a single objective optimization + - metric: RootMeanSquaredError .. code-block:: YAML # MultiObjective Optimization config - optimization_options: - objective_options: - objectives: - # List all of your metrics here, - # only list multiple objectives for a multi objective optimization - - metric: RMSE - - metric: R2 + objective: + metrics: + # List all of your metrics here, + # only list multiple objectives for a multi objective optimization + - metric: RMSE + - metric: R2 PassThrough Metric ****************** @@ -47,14 +45,13 @@ .. code-block:: YAML # Single objective optimization config - optimization_options: - objective_options: - objectives: - # List all of your metrics here, - # only list 1 metric for a single objective optimization - - metric: PassThrough - name: foo # optional, any name will work - minimize: False + objective: + metrics: + # List all of your metrics here, + # only list 1 metric for a single objective optimization + - metric: PassThrough + name: foo # optional, any name will work + minimize: False You could also simply specify a name, with no metric type and it will default to a pass through metric: @@ -62,12 +59,11 @@ .. code-block:: YAML # Single objective optimization config - optimization_options: - objective_options: - objectives: - # List all of your metrics here, - # only list 1 metric for a single objective optimization - - name: foo # optional, any name will work + objective: + metrics: + # List all of your metrics here, + # only list 1 metric for a single objective optimization + - name: foo # optional, any name will work If working in a language agnostic way, you can write out your output.json file like this (see more at :mod:`Wrappers `): diff --git a/boa/plot.py b/boa/plot.py index 467967e..658b326 100644 --- a/boa/plot.py +++ b/boa/plot.py @@ -15,10 +15,13 @@ import click import panel as pn +import boa.plotting from boa.plotting import app_view -@click.command() +@click.command( + epilog=f"Name of Plots to be added here: {', '.join(plot for plot in boa.plotting.__all__ if plot != 'app_view')}" +) @click.option( "-sp", "--scheduler-path", @@ -27,6 +30,11 @@ help="Path to scheduler json file.", ) def main(scheduler_path): + """ + Launch a basic EDA plot view of your optimization. + Creating a web app with the scheduler json file. + + """ template = app_view(scheduler=scheduler_path) pn.serve({pathlib.Path(__file__).name: template}) diff --git a/boa/plotting.py b/boa/plotting.py index 8c3eb02..e36454d 100644 --- a/boa/plotting.py +++ b/boa/plotting.py @@ -35,6 +35,16 @@ pn.extension("plotly") +__all__ = [ + "plot_contours", + "plot_metrics_trace", + "plot_pareto_frontier", + "plot_slice", + "scheduler_to_df", + "app_view", +] + + def _maybe_load_scheduler(scheduler: SchedulerOrPath): if isinstance(scheduler, PathLike_tup): scheduler = scheduler_from_json_file(scheduler) @@ -414,7 +424,10 @@ def app_view( pareto = plot_pareto_frontier(scheduler=scheduler, metric_names=metric_names) else: pareto = None - view.append(pn.Row(plot_metrics_trace(schedulers=scheduler, metric_names=metric_names), pareto)) + row1 = pn.Row(plot_metrics_trace(schedulers=scheduler, metric_names=metric_names)) + if pareto: + row1.append(pareto) + view.append(row1) view.append(plot_slice(scheduler=scheduler)) view.append(plot_contours(scheduler=scheduler, metric_names=metric_names)) view.append(scheduler_to_df(scheduler)) diff --git a/boa/wrappers/base_wrapper.py b/boa/wrappers/base_wrapper.py index c2807a4..781d773 100644 --- a/boa/wrappers/base_wrapper.py +++ b/boa/wrappers/base_wrapper.py @@ -145,7 +145,7 @@ def load_config(self, config_path: PathLike, *args, **kwargs) -> BOAConfig: your configuration dataclass. Load_config will (unless overwritten in a subclass), do some basic "normalizations" - to your configuration for convenience. See :func:`.normalize_config` + to your configuration for convenience. See :class:`.BOAConfig` and its __init__ method for more information about how the normalization works and what config options you can control. diff --git a/docs/code_reference.rst b/docs/code_reference.rst index 53c656b..f41a0cf 100644 --- a/docs/code_reference.rst +++ b/docs/code_reference.rst @@ -54,6 +54,7 @@ Plotting your Experiment :toctree: api :template: custom_module_template_short.rst + boa.plot boa.plotting @@ -71,3 +72,4 @@ Advanced Usage/Direct Python Access boa.utils boa.metaclasses boa.instantiation_base + boa.template diff --git a/docs/conf.py b/docs/conf.py index 209f920..bdeccee 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -136,7 +136,8 @@ "icon": "fab fa-github-square", # Whether icon should be a FontAwesome class, or a local file } - ] + ], + "show_nav_level": 2, } @@ -153,6 +154,7 @@ "torch": ("https://pytorch.org/docs/stable", None), "panel": ("https://panel.holoviz.org", None), "pandas": ("https://pandas.pydata.org/docs", None), + "jinja2": ("https://jinja.palletsprojects.com/", None), } # BOA things diff --git a/docs/examples/1run_r_streamlined.ipynb b/docs/examples/1run_r_streamlined.ipynb index cf64db6..32802a5 100644 --- a/docs/examples/1run_r_streamlined.ipynb +++ b/docs/examples/1run_r_streamlined.ipynb @@ -12,7 +12,7 @@ "This notebook demonstrates how to:\n", "\n", "Write a basic Wrapper script in R and have BOA launch your optimization using the BOA CLI interface. See {mod}`instructions for creating a model wrapper <.boa.wrappers>` for more details about creating a wrapper script.\n", - "You can also look at {doc}`instructions for configurations files` for more details on creating a configuration file." + "You can also look at {doc}`instructions for configurations files` for more details on creating a configuration file." ] }, { diff --git a/docs/examples/default_config.yaml b/docs/examples/default_config.yaml index 5913af3..de9b914 100644 --- a/docs/examples/default_config.yaml +++ b/docs/examples/default_config.yaml @@ -351,12 +351,6 @@ scheduler: global_stopping_strategy: '...' suppress_storage_errors_after_retries: '...' -# ###### -# name # -# ###### - -name: '...' - # ####################### # parameter_constraints # # ####################### @@ -405,6 +399,9 @@ script_options: # (if neither experiment_dir nor output_dir are specified, output_dir defaults # to whatever pwd returns (and equivalent on windows)) output_dir: '...' + # name of the experiment. Used with output_dir to create the experiment directory + # if experiment_dir is not specified. + exp_name: '...' # Whether to append a timestamp to the output directory to ensure uniqueness. # Defaults to `True` if not specified. append_timestamp: '...' diff --git a/docs/user_guide/configuration.rst b/docs/user_guide/configuration.rst deleted file mode 100644 index 5a37bb9..0000000 --- a/docs/user_guide/configuration.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _configuration: - -################### -Configuration Guide -################### - -*********************** -Example Configurations -*********************** - -This configuration can be generated from BOA with the following command:: - - python -m boa.config --output-path [path to output] - -Default Configuration -============================== - -.. literalinclude:: ../examples/default_config.yaml - :language: YAML - - -******************************** -Configuration Detailed Reference -******************************** diff --git a/docs/user_guide/package_overview.rst b/docs/user_guide/package_overview.rst index cd0d964..e55849e 100644 --- a/docs/user_guide/package_overview.rst +++ b/docs/user_guide/package_overview.rst @@ -11,10 +11,14 @@ Creating a configuration File Configuration is usually done through a configuration file in YAML or json format. The goal of :doc:`BOA ` is allow as much functionality of the bayesian optimization through the configuration as possible. Allowing users to worry mostly about their model itself instead of the optimization. In the configuration file you can do a single objective, multi objective, or scalarized objective optimization. You can specify different acquisition functions, or let them be selected for you, what parameters you are optimizing, parameter constraints, outcome constraints, and more. -See the :doc:`instructions for configurations files` for details. + +.. toctree:: + :maxdepth: 0 + + /api/boa.config .. note:: - We are adding more and more functionality to the configuration directly, but if there are features not supported in the boa configuration file yet, but are support in the underlying Ax or BoTorch libraries, you can customize things further with your run script. See below. + We are adding more and more functionality to the configuration directly, but if there are features not supported in the boa configuration file yet, but are support in the underlying Ax or BoTorch libraries, you can customize things further with your lauch script. See below. Objective functions @@ -36,25 +40,25 @@ and there is a standard interface to follow. See the :mod:`instructions for creating a model wrapper <.boa.wrappers>` for details. -********************************************* -Creating a run script (Usually Not Needed) -********************************************* +**************************************************** +Creating a Python launch script (Usually Not Needed) +**************************************************** -Most of the time you won't need to write a run script because BOA has an built-in run script in -its :mod:`.controller`. But if you do need more control over your run script than the default -provides, you can either subclass :class:`.Controller` or write your own run script. Subclassing +Most of the time you won't need to write a launch script because BOA has an built-in launch script in +its :mod:`.controller` that is called when calling `python -m boa`. But if you do need more control over your launch script than the default +provides, you can either subclass :class:`.Controller` or write your own launch script. Subclassing :class:`.Controller` might be easier if you just need to modify :meth:`.Controller.run` or :meth:`.Controller.initialize_wrapper` or :meth:`.Controller.initialize_scheduler` -but can utilize the rest of the functions. If you need a lot of customization, writing your own run script might be -easier. Some Custom run scripts are included in the link below. +but can utilize the rest of the functions. If you need a lot of customization, writing your own script might be +easier. Some Custom scripts are included in the link below. -See :doc:`examples of custom run scripts ` for details. +See :doc:`examples of custom launch scripts ` for details. ********************************************* Starting From Command Line ********************************************* -If you are using :doc:`BOA's ` in built :mod:`.controller` for your run script, +If you are using :doc:`BOA's ` in built :mod:`.controller` for your launch script, you can start your run easily from the command line. With your conda environment for boa activated, run:: @@ -83,7 +87,6 @@ A fuller example using the command line interface can be found :doc:`here