Skip samples when using DOE#

from __future__ import annotations

from typing import TYPE_CHECKING

from numpy import array
from numpy import sqrt

from gemseo import create_design_space
from gemseo import create_scenario
from gemseo.core.discipline import Discipline
from gemseo.settings.doe import CustomDOE_Settings
from gemseo.settings.post import BasicHistory_Settings

if TYPE_CHECKING:
    from gemseo.typing import StrKeyMapping

In this example, we show how to skip the evaluation at a DOE point during a DOEScenario run. This is useful in situations where the evaluation of a sample fails or when the user wants to avoid evaluating some samples when certain conditions are met. The DOE algorithms in GEMSEO are able to catch ValueError exceptions (and only this specific type of exception) at runtime and move to the next sample.

Let us consider a discipline implementing the function \(y=sqrt(a)\). The _run method of this discipline raises a ValueError when \(a < 0\). Of course, you may use any other set of conditions to raise the exception in your scripts.

class ValueErrorDiscipline(Discipline):
    default_grammar_type = Discipline.GrammarType.SIMPLE

    def __init__(self) -> None:
        super().__init__()
        self.input_grammar.update_from_names("a")
        self.output_grammar.update_from_names("y")

    def _run(self, input_data: StrKeyMapping):
        a = input_data["a"]
        if a < 0:
            msg = "The sample is undefined for a < 0."
            raise ValueError(msg)
        return {"y": sqrt(a)}


discipline = ValueErrorDiscipline()

We define a design space with the variable \(a\in[-1,10]\):

design_space = create_design_space()
design_space.add_variable(
    "a", type_=design_space.DesignVariableType.FLOAT, lower_bound=-1, upper_bound=10
)

We want to evaluate this discipline over this design space at points 1, -1 and 4:

samples = array([[1.0], [-1.0], [4.0]])

For that, we can create a scenario and execute it with a CustomDOE with the setting "samples":

scenario = create_scenario(
    [discipline],
    "y",
    design_space,
    scenario_type="DOE",
    formulation_name="DisciplinaryOpt",
)
custom_doe_settings = CustomDOE_Settings(samples=samples)
scenario.execute(custom_doe_settings)
    INFO - 16:25:02: *** Start DOEScenario execution ***
    INFO - 16:25:02: DOEScenario
    INFO - 16:25:02:    Disciplines: ValueErrorDiscipline
    INFO - 16:25:02:    MDO formulation: DisciplinaryOpt
    INFO - 16:25:02: Optimization problem:
    INFO - 16:25:02:    minimize y(a)
    INFO - 16:25:02:    with respect to a
    INFO - 16:25:02:    over the design space:
    INFO - 16:25:02:       +------+-------------+-------+-------------+-------+
    INFO - 16:25:02:       | Name | Lower bound | Value | Upper bound | Type  |
    INFO - 16:25:02:       +------+-------------+-------+-------------+-------+
    INFO - 16:25:02:       | a    |      -1     |  None |      10     | float |
    INFO - 16:25:02:       +------+-------------+-------+-------------+-------+
    INFO - 16:25:02: Solving optimization problem with algorithm CustomDOE:
    INFO - 16:25:02:     33%|███▎      | 1/3 [00:00<00:00, 3542.49 it/sec, feas=True, obj=1]
   ERROR - 16:25:02: Failed to evaluate function y
ValueError: The sample is undefined for a < 0.
   ERROR - 16:25:02: The evaluation of the functions at point [-1.] raised a ValueError; skipping to the next point.
ValueError: The sample is undefined for a < 0.
    INFO - 16:25:02:     67%|██████▋   | 2/3 [00:00<00:00, 1650.65 it/sec, feas=True, obj=2]
    INFO - 16:25:02: Optimization result:
    INFO - 16:25:02:    Optimizer info:
    INFO - 16:25:02:       Status: None
    INFO - 16:25:02:       Message: None
    INFO - 16:25:02:    Solution:
    INFO - 16:25:02:       Objective: 1.0
    INFO - 16:25:02:       Design space:
    INFO - 16:25:02:          +------+-------------+-------+-------------+-------+
    INFO - 16:25:02:          | Name | Lower bound | Value | Upper bound | Type  |
    INFO - 16:25:02:          +------+-------------+-------+-------------+-------+
    INFO - 16:25:02:          | a    |      -1     |   1   |      10     | float |
    INFO - 16:25:02:          +------+-------------+-------+-------------+-------+
    INFO - 16:25:02: *** End DOEScenario execution (time: 0:00:00.003329) ***

The logger shows that GEMSEO ignores the ValueError raised at the second point and switches to the third point. The post-processing of the scenario only includes two runs:

basic_history_settings = BasicHistory_Settings(
    variable_names=["y"], save=False, show=True
)
scenario.post_process(basic_history_settings)
History plot
<gemseo.post.basic_history.BasicHistory object at 0x7c2f8ef94d70>

Warning

In order to be able to continue the execution of a DOEScenario, the execution status and statistics of disciplines must be disabled. This is the default behavior, but you can also disable them explicitly with the configure() function.

Note that if your discipline is able to return a NaN without raising any exceptions, you do not need to use the mechanism explained here, in that case the NaN values will be handled by GEMSEO and stored in the database of the scenario.

Total running time of the script: (0 minutes 0.088 seconds)

Gallery generated by Sphinx-Gallery