Exploration setup
=================
This section covers the basic workflow of setting up an optimas
:class:`~optimas.explorations.Exploration`, which is typically used to launch
an optimization or parameter scan. This involves:
- Specifying the parameters that should be varied during the exploration.
- Specifying the optimization objectives and other parameters that should
analyzed for each evaluation.
- Choosing a generator. This determines the strategy with which new evaluations
are generated.
- Choosing an evaluator. This determines how the evaluations are performed and
which computational resources are assigned to them.
- Specifying how many evaluations should be carried out in parallel and the
criteria for ending the exploration.
Parameters to vary
~~~~~~~~~~~~~~~~~~
The parameters to vary (:class:`~optimas.core.VaryingParameter`) are the
parameters that should be tuned or scanned during the exploration.
For example, if we want to see how the outcome of an evaluation depends on two
parameters named ``x0`` and ``x1`` that can vary in the ranges [0, 15] and
[-5, 5], we would define them as
.. code-block:: python
from optimas.core import VaryingParameter
var_1 = VaryingParameter("x0", 0.0, 15.0)
var_2 = VaryingParameter("x1", -5.0, 5.0)
Objectives and other analyzed parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The objectives (:class:`~optimas.core.Objective`) define the outcomes of an
evaluation that optimas should optimize (maximize or minimize) or scan.
Optionally, a list of parameters (:class:`~optimas.core.Parameter`) that do not
play a role in the optimization but that should be analyzed at each evaluation
(for example, because they provide useful information about the evaluations)
can also be given.
The following code shows how to define one objective ``'f'`` that
should be minimized and two diagnostics ``'diag_1'`` and ``'diag_2'`` that will
also be calculated for each evaluation.
.. code-block:: python
from optimas.core import Objective, Parameter
obj = Objective("f", minimize=True)
diag_1 = Parameter("diag_1")
diag_2 = Parameter("diag_2")
Generator
~~~~~~~~~
The generator defines the strategy with which new points are generated
during the exploration. There are multiple generators implemented in optimas
(see :ref:`generators`) that allow for various optimization strategies or
parameter scans.
In the example below, the varying parameters, objectives and diagnostics
defined in the previous sections are used to set up a single-fidelity Bayesian
optimizer based on `Ax `_.
``n_init=4`` indicates that 4 random samples will be generated before the
Bayesian optimization loop is started (see
:class:`~optimas.generators.AxSingleFidelityGenerator` for more details).
.. code-block:: python
from optimas.generators import AxSingleFidelityGenerator
gen = AxSingleFidelityGenerator(
varying_parameters=[var_1, var_2],
objectives=[obj],
analyzed_parameters=[diag_1, diag_2],
n_init=4,
)
Evaluator
~~~~~~~~~
The evaluator is in charge of getting the trials suggested by the generator and
evaluating them, returning the value of the objectives and other analyzed
parameters.
There are two types of evaluators:
- :class:`~optimas.evaluators.FunctionEvaluator`: used to evaluate Python
functions that do not demand large computational resources. Each evaluation
will be carried out in a different process using either
`multiprocessing `_
or MPI.
- :class:`~optimas.evaluators.TemplateEvaluator`: used to carry out expensive
evaluations that are executed by running an external script. In this case, a
template script should be given from which the scripts of each evaluation
will be generated.
Each evaluation is executed using MPI with the amount or resources (number of
processes and GPUs) specified by the user. After executing the script, the
output of the evaluation is analyzed with a user-defined function that
calculates the value of the objectives and other analyzed parameters.
See :ref:`optimas-with-simulations` for more details about how to use a
:class:`~optimas.evaluators.TemplateEvaluator`.
The code below shows an example of how to define a
:class:`~optimas.evaluators.TemplateEvaluator` that executes a script generated
from the template ``'template_simulation_script.py'`` and whose output is
analyzed by a function ``analyze_simulation``. The script is executed with MPI,
using by default a single process and no GPUs. This can be
changed by specifying the ``n_procs`` and ``n_gpus`` attributes.
.. code-block:: python
from optimas.evaluators import TemplateEvaluator
ev = TemplateEvaluator(
sim_template="template_simulation_script.py",
analysis_func=analyze_simulation,
# n_procs=2,
# n_gpus=2
)
Exploration
~~~~~~~~~~~
The :class:`~optimas.explorations.Exploration` is the main class that
coordinates the generation and execution of evaluations. In addition to
the generator and evaluator to use, it requires the user to specify the maximum
number evaluations to perform and the number of simulation workers.
In the example below, a maximum of 100 evaluations will be carried out using 4
simulation workers. This means that up to 4 evaluation will be performed in
parallel at any time.
.. code-block:: python
from optimas.explorations import Exploration
exp = Exploration(generator=gen, evaluator=ev, max_evals=100, sim_workers=4)
The exploration is started by executing ``exp.run()`` inside a
``if __name__ == '__main__':`` block:
.. code-block:: python
if __name__ == "__main__":
exp.run()
This is needed in order to safely execute the exploration in systems using the
``'spawn'``
`multiprocessing `_
method (default on macOS).