.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "examples/surrogate/plot_surrogate_scenario.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_surrogate_plot_surrogate_scenario.py: Plug a surrogate discipline in a Scenario ========================================= In this section we describe the usage of surrogate model in |g|, which is implemented in the :class:`.SurrogateDiscipline` class. A :class:`.SurrogateDiscipline` can be used to substitute a :class:`.MDODiscipline` within a :class:`.Scenario`. This :class:`.SurrogateDiscipline` is an evaluation of the :class:`.MDODiscipline` and is faster to compute than the original discipline. It relies on a :class:`.BaseMLRegressionAlgo`. This comes at the price of computing a :term:`DOE` on the original :class:`.MDODiscipline`, and validating the approximation. The computations from which the approximation is built can be available, or can be built using |g|' :term:`DOE` capabilities. See :ref:`sobieski_doe` and :ref:`sellar_mdo`. In |g|'s, the data used to build the surrogate model is taken from a :class:`.Dataset` containing both inputs and outputs of the :term:`DOE`. This :class:`.Dataset` may have been generated by |g| from a cache, using the :meth:`.AbstractFullCache.to_dataset` method, from a database, using the :meth:`.OptimizationProblem.to_dataset` method, or from a NumPy array or a text file using the :meth:`.Dataset.from_array` and :meth:`.Dataset.from_txt`. Then, the surrogate discipline can be used as any other discipline in a :class:`.MDOScenario`, a :class:`.DOEScenario`, or a :class:`.BaseMDA`. .. GENERATED FROM PYTHON SOURCE LINES 50-67 .. code-block:: Python from __future__ import annotations from numpy import array from numpy import hstack from numpy import vstack from gemseo import configure_logger from gemseo import create_discipline from gemseo import create_scenario from gemseo import create_surrogate from gemseo.datasets.io_dataset import IODataset from gemseo.problems.mdo.sobieski.core.design_space import SobieskiDesignSpace configure_logger() .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 68-80 Create a surrogate discipline ----------------------------- Create the learning dataset ~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you already have available data from a :term:`DOE` produced externally, it is possible to create a :class:`.Dataset` and Step 1 ends here. For example, let us consider a synthetic dataset, with :math:`x` as input and :math:`y` as output, described as a numpy array. Then, we store these data in a :class:`.Dataset`: .. GENERATED FROM PYTHON SOURCE LINES 80-89 .. code-block:: Python variables = ["x", "y"] sizes = {"x": 1, "y": 1} groups = {"x": "inputs", "y": "outputs"} data = vstack(( hstack((array([1.0]), array([1.0]))), hstack((array([2.0]), array([2.0]))), )) synthetic_dataset = IODataset.from_array(data, variables, sizes, groups) .. GENERATED FROM PYTHON SOURCE LINES 90-109 If you do not have available data,the following paragraphs of Step 1 concern you. Here, we illustrate the generation of the training data using a :class:`.DOEScenario`, similarly to :ref:`sobieski_doe`, where more details are given. In this basic example, an :class:`.MDODiscipline` computing the mission performance (range) in the :ref:`SSBJ test case ` is sampled with a :class:`.DOEScenario`. Then, the generated database is used to build a :class:`.SurrogateDiscipline`. But more complex scenarios can be used in the same way: complete optimization processes or MDAs can be replaced by their surrogate counterparts. The right cache or database shall then be used to build the :class:`.SurrogateDiscipline`, but the main logic won't differ from this example. Firstly, we create the :class:`.MDODiscipline` by means of the API function :func:`.create_discipline`: .. GENERATED FROM PYTHON SOURCE LINES 109-112 .. code-block:: Python discipline = create_discipline("SobieskiMission") .. GENERATED FROM PYTHON SOURCE LINES 113-120 .. _surrogates_design_space: Then, we read the :class:`.DesignSpace` of the :ref:`Sobieski problem ` and keep only the inputs of the Sobieski Mission "x_shared", "y_24", "y_34" as inputs of the DOE: .. GENERATED FROM PYTHON SOURCE LINES 121-124 .. code-block:: Python design_space = SobieskiDesignSpace() design_space = design_space.filter(["x_shared", "y_24", "y_34"]) .. GENERATED FROM PYTHON SOURCE LINES 125-129 From this :class:`.MDODiscipline` and this :class:`.DesignSpace`, we build a :class:`.DOEScenario` by means of the API function :func:`.create_scenario`: .. GENERATED FROM PYTHON SOURCE LINES 130-138 .. code-block:: Python scenario = create_scenario( [discipline], "DisciplinaryOpt", "y_4", design_space, scenario_type="DOE", ) .. GENERATED FROM PYTHON SOURCE LINES 139-140 Lastly, we execute the process with the :term:`LHS` algorithm and 30 samples. .. GENERATED FROM PYTHON SOURCE LINES 140-143 .. code-block:: Python scenario.execute({"n_samples": 30, "algo": "lhs"}) mission_dataset = scenario.to_dataset(opt_naming=False) .. rst-class:: sphx-glr-script-out .. code-block:: none INFO - 08:57:38: INFO - 08:57:38: *** Start DOEScenario execution *** INFO - 08:57:38: DOEScenario INFO - 08:57:38: Disciplines: SobieskiMission INFO - 08:57:38: MDO formulation: DisciplinaryOpt INFO - 08:57:38: Optimization problem: INFO - 08:57:38: minimize y_4(x_shared, y_24, y_34) INFO - 08:57:38: with respect to x_shared, y_24, y_34 INFO - 08:57:38: over the design space: INFO - 08:57:38: +-------------+-------------+------------+-------------+-------+ INFO - 08:57:38: | Name | Lower bound | Value | Upper bound | Type | INFO - 08:57:38: +-------------+-------------+------------+-------------+-------+ INFO - 08:57:38: | x_shared[0] | 0.01 | 0.05 | 0.09 | float | INFO - 08:57:38: | x_shared[1] | 30000 | 45000 | 60000 | float | INFO - 08:57:38: | x_shared[2] | 1.4 | 1.6 | 1.8 | float | INFO - 08:57:38: | x_shared[3] | 2.5 | 5.5 | 8.5 | float | INFO - 08:57:38: | x_shared[4] | 40 | 55 | 70 | float | INFO - 08:57:38: | x_shared[5] | 500 | 1000 | 1500 | float | INFO - 08:57:38: | y_24 | 0.44 | 4.15006276 | 11.13 | float | INFO - 08:57:38: | y_34 | 0.44 | 1.10754577 | 1.98 | float | INFO - 08:57:38: +-------------+-------------+------------+-------------+-------+ INFO - 08:57:38: Solving optimization problem with algorithm lhs: INFO - 08:57:38: 3%|▎ | 1/30 [00:00<00:00, 331.96 it/sec, obj=1.23e+3] INFO - 08:57:38: 7%|▋ | 2/30 [00:00<00:00, 545.57 it/sec, obj=2.09e+3] INFO - 08:57:38: 10%|█ | 3/30 [00:00<00:00, 706.87 it/sec, obj=792] INFO - 08:57:38: 13%|█▎ | 4/30 [00:00<00:00, 822.94 it/sec, obj=387] INFO - 08:57:38: 17%|█▋ | 5/30 [00:00<00:00, 921.26 it/sec, obj=510] INFO - 08:57:38: 20%|██ | 6/30 [00:00<00:00, 1001.82 it/sec, obj=1.27e+3] INFO - 08:57:38: 23%|██▎ | 7/30 [00:00<00:00, 1058.63 it/sec, obj=2.56e+3] INFO - 08:57:38: 27%|██▋ | 8/30 [00:00<00:00, 1115.17 it/sec, obj=1.88e+3] INFO - 08:57:38: 30%|███ | 9/30 [00:00<00:00, 1163.65 it/sec, obj=720] INFO - 08:57:38: 33%|███▎ | 10/30 [00:00<00:00, 1206.20 it/sec, obj=1.33e+3] INFO - 08:57:38: 37%|███▋ | 11/30 [00:00<00:00, 1243.23 it/sec, obj=436] INFO - 08:57:38: 40%|████ | 12/30 [00:00<00:00, 1276.03 it/sec, obj=254] INFO - 08:57:38: 43%|████▎ | 13/30 [00:00<00:00, 1305.20 it/sec, obj=420] INFO - 08:57:38: 47%|████▋ | 14/30 [00:00<00:00, 1325.18 it/sec, obj=655] INFO - 08:57:38: 50%|█████ | 15/30 [00:00<00:00, 1348.51 it/sec, obj=93.2] INFO - 08:57:38: 53%|█████▎ | 16/30 [00:00<00:00, 1369.79 it/sec, obj=1.33e+3] INFO - 08:57:38: 57%|█████▋ | 17/30 [00:00<00:00, 1389.19 it/sec, obj=690] INFO - 08:57:38: 60%|██████ | 18/30 [00:00<00:00, 1407.17 it/sec, obj=107] INFO - 08:57:38: 63%|██████▎ | 19/30 [00:00<00:00, 1423.75 it/sec, obj=213] INFO - 08:57:38: 67%|██████▋ | 20/30 [00:00<00:00, 1438.45 it/sec, obj=2.24e+3] INFO - 08:57:38: 70%|███████ | 21/30 [00:00<00:00, 1445.67 it/sec, obj=860] INFO - 08:57:38: 73%|███████▎ | 22/30 [00:00<00:00, 1457.85 it/sec, obj=71.2] INFO - 08:57:38: 77%|███████▋ | 23/30 [00:00<00:00, 1469.91 it/sec, obj=861] INFO - 08:57:38: 80%|████████ | 24/30 [00:00<00:00, 1481.10 it/sec, obj=719] INFO - 08:57:38: 83%|████████▎ | 25/30 [00:00<00:00, 1491.68 it/sec, obj=153] INFO - 08:57:38: 87%|████████▋ | 26/30 [00:00<00:00, 1501.76 it/sec, obj=517] INFO - 08:57:38: 90%|█████████ | 27/30 [00:00<00:00, 1511.18 it/sec, obj=716] INFO - 08:57:38: 93%|█████████▎| 28/30 [00:00<00:00, 1516.54 it/sec, obj=324] INFO - 08:57:38: 97%|█████████▋| 29/30 [00:00<00:00, 1524.32 it/sec, obj=432] INFO - 08:57:38: 100%|██████████| 30/30 [00:00<00:00, 1532.18 it/sec, obj=1.27e+3] INFO - 08:57:38: Optimization result: INFO - 08:57:38: Optimizer info: INFO - 08:57:38: Status: None INFO - 08:57:38: Message: None INFO - 08:57:38: Number of calls to the objective function by the optimizer: 30 INFO - 08:57:38: Solution: INFO - 08:57:38: Objective: 71.16601799429675 INFO - 08:57:38: Design space: INFO - 08:57:38: +-------------+-------------+---------------------+-------------+-------+ INFO - 08:57:38: | Name | Lower bound | Value | Upper bound | Type | INFO - 08:57:38: +-------------+-------------+---------------------+-------------+-------+ INFO - 08:57:38: | x_shared[0] | 0.01 | 0.04440901205483268 | 0.09 | float | INFO - 08:57:38: | x_shared[1] | 30000 | 58940.10748233336 | 60000 | float | INFO - 08:57:38: | x_shared[2] | 1.4 | 1.441133922818264 | 1.8 | float | INFO - 08:57:38: | x_shared[3] | 2.5 | 5.893919149663935 | 8.5 | float | INFO - 08:57:38: | x_shared[4] | 40 | 58.55971698205414 | 70 | float | INFO - 08:57:38: | x_shared[5] | 500 | 598.9420525239799 | 1500 | float | INFO - 08:57:38: | y_24 | 0.44 | 0.8060924457095278 | 11.13 | float | INFO - 08:57:38: | y_34 | 0.44 | 1.458803878476488 | 1.98 | float | INFO - 08:57:38: +-------------+-------------+---------------------+-------------+-------+ INFO - 08:57:38: *** End DOEScenario execution (time: 0:00:00.034166) *** .. GENERATED FROM PYTHON SOURCE LINES 144-153 .. seealso:: In this tutorial, the :term:`DOE` is based on `pyDOE `_, however, several other designs are available, based on the package or `OpenTURNS `_. Some examples of these designs are plotted in :ref:`doe_algos`. To list the available :term:`DOE` algorithms in the current |g| configuration, use :meth:`gemseo.get_available_doe_algorithms`. .. GENERATED FROM PYTHON SOURCE LINES 155-169 Create the :class:`.SurrogateDiscipline` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From this :class:`.Dataset`, we can build a :class:`.SurrogateDiscipline` of the :class:`.MDODiscipline`. Indeed, by means of the API function :class:`~gemseo.create_surrogate`, we create the :class:`.SurrogateDiscipline` from the dataset, which can be executed as any other :term:`discipline`. Precisely, by means of the API function :func:`.create_surrogate`, we create a :class:`.SurrogateDiscipline` relying on a :class:`.LinearRegressor` and inheriting from :class:`.MDODiscipline`: .. GENERATED FROM PYTHON SOURCE LINES 169-172 .. code-block:: Python synthetic_surrogate = create_surrogate("LinearRegressor", synthetic_dataset) .. rst-class:: sphx-glr-script-out .. code-block:: none INFO - 08:57:38: Build the surrogate discipline: LinReg_IODataset INFO - 08:57:38: Dataset size: 2 INFO - 08:57:38: Surrogate model: LinearRegressor INFO - 08:57:38: Use the surrogate discipline: LinReg_IODataset INFO - 08:57:38: Inputs: x INFO - 08:57:38: Outputs: y INFO - 08:57:38: Jacobian: use surrogate model jacobian .. GENERATED FROM PYTHON SOURCE LINES 173-180 .. seealso:: Note that a subset of the inputs and outputs to be used to build the :class:`.SurrogateDiscipline` may be specified by the user if needed, mainly to avoid unnecessary computations. Then, we execute it as any :class:`.MDODiscipline`: .. GENERATED FROM PYTHON SOURCE LINES 180-184 .. code-block:: Python input_data = {"x": array([2.0])} out = synthetic_surrogate.execute(input_data) out["y"] .. rst-class:: sphx-glr-script-out .. code-block:: none array([2.]) .. GENERATED FROM PYTHON SOURCE LINES 185-188 In our study case, from the :term:`DOE` built at Step 1, we build a :class:`.RBFRegressor` of :math:`y_4` representing the range in function of L/D: .. GENERATED FROM PYTHON SOURCE LINES 188-190 .. code-block:: Python range_surrogate = create_surrogate("RBFRegressor", mission_dataset) .. rst-class:: sphx-glr-script-out .. code-block:: none INFO - 08:57:38: Build the surrogate discipline: RBF_DOEScenario INFO - 08:57:38: Dataset size: 30 INFO - 08:57:38: Surrogate model: RBFRegressor INFO - 08:57:38: Use the surrogate discipline: RBF_DOEScenario INFO - 08:57:38: Inputs: x_shared, y_24, y_34 INFO - 08:57:38: Outputs: y_4 INFO - 08:57:38: Jacobian: use surrogate model jacobian .. GENERATED FROM PYTHON SOURCE LINES 191-198 Use the :class:`.SurrogateDiscipline` in MDO -------------------------------------------- The obtained :class:`.SurrogateDiscipline` can be used in any :class:`.Scenario`, such as a :class:`.DOEScenario` or :class:`.MDOScenario`. We see here that the :meth:`.MDODiscipline.execute` method can be used as in any other discipline to compute the outputs for given inputs: .. GENERATED FROM PYTHON SOURCE LINES 198-204 .. code-block:: Python for i in range(5): lod = i * 2.0 y_4_pred = range_surrogate.execute({"y_24": array([lod])})["y_4"] print(f"Surrogate range (L/D = {lod}) = {y_4_pred}") .. rst-class:: sphx-glr-script-out .. code-block:: none Surrogate range (L/D = 0.0) = [-97.86844673] Surrogate range (L/D = 2.0) = [184.60105962] Surrogate range (L/D = 4.0) = [505.37518268] Surrogate range (L/D = 6.0) = [840.33241658] Surrogate range (L/D = 8.0) = [1161.49215263] .. GENERATED FROM PYTHON SOURCE LINES 205-210 And we can build and execute an optimization scenario from it. The design variables are "y_24". The Jacobian matrix is computed by finite differences by default for surrogates, except for the :class:`.SurrogateDiscipline` relying on :class:`.LinearRegressor` which has an analytical (and constant) Jacobian. .. GENERATED FROM PYTHON SOURCE LINES 210-220 .. code-block:: Python design_space = design_space.filter(["y_24"]) scenario = create_scenario( range_surrogate, "DisciplinaryOpt", "y_4", design_space, maximize_objective=True, ) scenario.execute({"max_iter": 30, "algo": "L-BFGS-B"}) .. rst-class:: sphx-glr-script-out .. code-block:: none INFO - 08:57:38: INFO - 08:57:38: *** Start MDOScenario execution *** INFO - 08:57:38: MDOScenario INFO - 08:57:38: Disciplines: RBF_DOEScenario INFO - 08:57:38: MDO formulation: DisciplinaryOpt INFO - 08:57:38: Optimization problem: INFO - 08:57:38: minimize -y_4(y_24) INFO - 08:57:38: with respect to y_24 INFO - 08:57:38: over the design space: INFO - 08:57:38: +------+-------------+--------------------+-------------+-------+ INFO - 08:57:38: | Name | Lower bound | Value | Upper bound | Type | INFO - 08:57:38: +------+-------------+--------------------+-------------+-------+ INFO - 08:57:38: | y_24 | 0.44 | 0.8060924457095278 | 11.13 | float | INFO - 08:57:38: +------+-------------+--------------------+-------------+-------+ INFO - 08:57:38: Solving optimization problem with algorithm L-BFGS-B: INFO - 08:57:38: 3%|▎ | 1/30 [00:00<00:00, 365.93 it/sec, obj=-10.3] INFO - 08:57:38: 7%|▋ | 2/30 [00:00<00:00, 251.42 it/sec, obj=-1.59e+3] INFO - 08:57:38: Optimization result: INFO - 08:57:38: Optimizer info: INFO - 08:57:38: Status: 0 INFO - 08:57:38: Message: CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL INFO - 08:57:38: Number of calls to the objective function by the optimizer: 3 INFO - 08:57:38: Solution: INFO - 08:57:38: Objective: -1589.7138353791017 INFO - 08:57:38: Design space: INFO - 08:57:38: +------+-------------+-------+-------------+-------+ INFO - 08:57:38: | Name | Lower bound | Value | Upper bound | Type | INFO - 08:57:38: +------+-------------+-------+-------------+-------+ INFO - 08:57:38: | y_24 | 0.44 | 11.13 | 11.13 | float | INFO - 08:57:38: +------+-------------+-------+-------------+-------+ INFO - 08:57:38: *** End MDOScenario execution (time: 0:00:00.020941) *** {'max_iter': 30, 'algo': 'L-BFGS-B'} .. GENERATED FROM PYTHON SOURCE LINES 221-263 Available surrogate models -------------------------- Currently, the following surrogate models are available: - Linear regression, based on the `Scikit-learn `_ library, for that use the :class:`.LinearRegressor` class. - Polynomial regression, based on the `Scikit-learn `_ library, for that use the :class:`.PolynomialRegressor` class, - Gaussian processes (also known as Kriging), based on the `Scikit-learn `_ library, for that use the :class:`.GaussianProcessRegressor` class, - Mixture of experts, for that use the :class:`.MOERegressor` class, - Random forest models, based on the `Scikit-learn # `_ library, for that use the :class:`.RandomForestRegressor` class. - RBF models (Radial Basis Functions), using the `SciPy `_ library, for that use the :class:`.RBFRegressor` class. - PCE models (Polynomial Chaos Expansion), based on the `OpenTURNS `_ library, for that use the :class:`.PCERegressor` class. To understand the detailed behavior of the models, please go to the documentation of the used packages. Extending surrogate models -------------------------- All surrogate models work the same way: the :class:`.BaseMLRegressionAlgo` base class shall be extended. See :ref:`extending-gemseo` to learn how to run |g| with external Python modules. Then, the :class:`.RegressionModelFactory` can build the new :class:`.BaseMLRegressionAlgo` automatically from its regression algorithm name and options. This factory is called by the constructor of :class:`.SurrogateDiscipline`. .. seealso:: More generally, |g| provides extension mechanisms to integrate external :DOE and optimization algorithms, disciplines, MDAs and surrogate models. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.123 seconds) .. _sphx_glr_download_examples_surrogate_plot_surrogate_scenario.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_surrogate_scenario.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_surrogate_scenario.py ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_