.. 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:`.MLRegressionAlgo`. 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 an :class:`.MDA`. .. 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.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 - 13:56:21: INFO - 13:56:21: *** Start DOEScenario execution *** INFO - 13:56:21: DOEScenario INFO - 13:56:21: Disciplines: SobieskiMission INFO - 13:56:21: MDO formulation: DisciplinaryOpt INFO - 13:56:21: Optimization problem: INFO - 13:56:21: minimize y_4(x_shared, y_24, y_34) INFO - 13:56:21: with respect to x_shared, y_24, y_34 INFO - 13:56:21: over the design space: INFO - 13:56:21: +-------------+-------------+------------+-------------+-------+ INFO - 13:56:21: | Name | Lower bound | Value | Upper bound | Type | INFO - 13:56:21: +-------------+-------------+------------+-------------+-------+ INFO - 13:56:21: | x_shared[0] | 0.01 | 0.05 | 0.09 | float | INFO - 13:56:21: | x_shared[1] | 30000 | 45000 | 60000 | float | INFO - 13:56:21: | x_shared[2] | 1.4 | 1.6 | 1.8 | float | INFO - 13:56:21: | x_shared[3] | 2.5 | 5.5 | 8.5 | float | INFO - 13:56:21: | x_shared[4] | 40 | 55 | 70 | float | INFO - 13:56:21: | x_shared[5] | 500 | 1000 | 1500 | float | INFO - 13:56:21: | y_24 | 0.44 | 4.15006276 | 11.13 | float | INFO - 13:56:21: | y_34 | 0.44 | 1.10754577 | 1.98 | float | INFO - 13:56:21: +-------------+-------------+------------+-------------+-------+ INFO - 13:56:21: Solving optimization problem with algorithm lhs: INFO - 13:56:21: 3%|▎ | 1/30 [00:00<00:00, 290.85 it/sec, obj=1.23e+3] INFO - 13:56:21: 7%|▋ | 2/30 [00:00<00:00, 480.12 it/sec, obj=2.09e+3] INFO - 13:56:21: 10%|█ | 3/30 [00:00<00:00, 624.90 it/sec, obj=792] INFO - 13:56:21: 13%|█▎ | 4/30 [00:00<00:00, 738.27 it/sec, obj=387] INFO - 13:56:21: 17%|█▋ | 5/30 [00:00<00:00, 828.46 it/sec, obj=510] INFO - 13:56:21: 20%|██ | 6/30 [00:00<00:00, 894.44 it/sec, obj=1.27e+3] INFO - 13:56:21: 23%|██▎ | 7/30 [00:00<00:00, 954.18 it/sec, obj=2.56e+3] INFO - 13:56:21: 27%|██▋ | 8/30 [00:00<00:00, 1005.74 it/sec, obj=1.88e+3] INFO - 13:56:21: 30%|███ | 9/30 [00:00<00:00, 1050.03 it/sec, obj=720] INFO - 13:56:21: 33%|███▎ | 10/30 [00:00<00:00, 1088.52 it/sec, obj=1.33e+3] INFO - 13:56:21: 37%|███▋ | 11/30 [00:00<00:00, 1122.32 it/sec, obj=436] INFO - 13:56:21: 40%|████ | 12/30 [00:00<00:00, 1142.47 it/sec, obj=254] INFO - 13:56:21: 43%|████▎ | 13/30 [00:00<00:00, 1167.48 it/sec, obj=420] INFO - 13:56:21: 47%|████▋ | 14/30 [00:00<00:00, 1191.56 it/sec, obj=655] INFO - 13:56:21: 50%|█████ | 15/30 [00:00<00:00, 1213.44 it/sec, obj=93.2] INFO - 13:56:21: 53%|█████▎ | 16/30 [00:00<00:00, 1233.21 it/sec, obj=1.33e+3] INFO - 13:56:21: 57%|█████▋ | 17/30 [00:00<00:00, 1251.53 it/sec, obj=690] INFO - 13:56:21: 60%|██████ | 18/30 [00:00<00:00, 1268.29 it/sec, obj=107] INFO - 13:56:21: 63%|██████▎ | 19/30 [00:00<00:00, 1278.65 it/sec, obj=213] INFO - 13:56:21: 67%|██████▋ | 20/30 [00:00<00:00, 1292.01 it/sec, obj=2.24e+3] INFO - 13:56:21: 70%|███████ | 21/30 [00:00<00:00, 1304.99 it/sec, obj=860] INFO - 13:56:21: 73%|███████▎ | 22/30 [00:00<00:00, 1317.29 it/sec, obj=71.2] INFO - 13:56:21: 77%|███████▋ | 23/30 [00:00<00:00, 1328.79 it/sec, obj=861] INFO - 13:56:21: 80%|████████ | 24/30 [00:00<00:00, 1339.27 it/sec, obj=719] INFO - 13:56:21: 83%|████████▎ | 25/30 [00:00<00:00, 1345.90 it/sec, obj=153] INFO - 13:56:21: 87%|████████▋ | 26/30 [00:00<00:00, 1354.78 it/sec, obj=517] INFO - 13:56:21: 90%|█████████ | 27/30 [00:00<00:00, 1363.75 it/sec, obj=716] INFO - 13:56:21: 93%|█████████▎| 28/30 [00:00<00:00, 1371.97 it/sec, obj=324] INFO - 13:56:21: 97%|█████████▋| 29/30 [00:00<00:00, 1379.61 it/sec, obj=432] INFO - 13:56:21: 100%|██████████| 30/30 [00:00<00:00, 1386.61 it/sec, obj=1.27e+3] INFO - 13:56:21: Optimization result: INFO - 13:56:21: Optimizer info: INFO - 13:56:21: Status: None INFO - 13:56:21: Message: None INFO - 13:56:21: Number of calls to the objective function by the optimizer: 30 INFO - 13:56:21: Solution: INFO - 13:56:21: Objective: 71.16601799429675 INFO - 13:56:21: Design space: INFO - 13:56:21: +-------------+-------------+---------------------+-------------+-------+ INFO - 13:56:21: | Name | Lower bound | Value | Upper bound | Type | INFO - 13:56:21: +-------------+-------------+---------------------+-------------+-------+ INFO - 13:56:21: | x_shared[0] | 0.01 | 0.04440901205483268 | 0.09 | float | INFO - 13:56:21: | x_shared[1] | 30000 | 58940.10748233336 | 60000 | float | INFO - 13:56:21: | x_shared[2] | 1.4 | 1.441133922818264 | 1.8 | float | INFO - 13:56:21: | x_shared[3] | 2.5 | 5.893919149663935 | 8.5 | float | INFO - 13:56:21: | x_shared[4] | 40 | 58.55971698205414 | 70 | float | INFO - 13:56:21: | x_shared[5] | 500 | 598.9420525239799 | 1500 | float | INFO - 13:56:21: | y_24 | 0.44 | 0.8060924457095278 | 11.13 | float | INFO - 13:56:21: | y_34 | 0.44 | 1.458803878476488 | 1.98 | float | INFO - 13:56:21: +-------------+-------------+---------------------+-------------+-------+ INFO - 13:56:21: *** End DOEScenario execution (time: 0:00:00.038071) *** .. 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 - 13:56:21: Build the surrogate discipline: LinReg_IODataset INFO - 13:56:21: Dataset size: 2 INFO - 13:56:21: Surrogate model: LinearRegressor INFO - 13:56:21: Use the surrogate discipline: LinReg_IODataset INFO - 13:56:21: Inputs: x INFO - 13:56:21: Outputs: y INFO - 13:56:21: 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 - 13:56:21: Build the surrogate discipline: RBF_DOEScenario INFO - 13:56:21: Dataset size: 30 INFO - 13:56:21: Surrogate model: RBFRegressor INFO - 13:56:21: Use the surrogate discipline: RBF_DOEScenario INFO - 13:56:21: Inputs: x_shared, y_24, y_34 INFO - 13:56:21: Outputs: y_4 INFO - 13:56:21: 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 - 13:56:21: INFO - 13:56:21: *** Start MDOScenario execution *** INFO - 13:56:21: MDOScenario INFO - 13:56:21: Disciplines: RBF_DOEScenario INFO - 13:56:21: MDO formulation: DisciplinaryOpt INFO - 13:56:21: Optimization problem: INFO - 13:56:21: minimize -y_4(y_24) INFO - 13:56:21: with respect to y_24 INFO - 13:56:21: over the design space: INFO - 13:56:21: +------+-------------+--------------------+-------------+-------+ INFO - 13:56:21: | Name | Lower bound | Value | Upper bound | Type | INFO - 13:56:21: +------+-------------+--------------------+-------------+-------+ INFO - 13:56:21: | y_24 | 0.44 | 0.8060924457095278 | 11.13 | float | INFO - 13:56:21: +------+-------------+--------------------+-------------+-------+ INFO - 13:56:21: Solving optimization problem with algorithm L-BFGS-B: INFO - 13:56:21: 3%|▎ | 1/30 [00:00<00:00, 306.58 it/sec, obj=-10.3] INFO - 13:56:21: 7%|▋ | 2/30 [00:00<00:00, 221.05 it/sec, obj=-1.59e+3] INFO - 13:56:21: Optimization result: INFO - 13:56:21: Optimizer info: INFO - 13:56:21: Status: 0 INFO - 13:56:21: Message: CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL INFO - 13:56:21: Number of calls to the objective function by the optimizer: 3 INFO - 13:56:21: Solution: INFO - 13:56:21: Objective: -1589.7138353791017 INFO - 13:56:21: Design space: INFO - 13:56:21: +------+-------------+-------+-------------+-------+ INFO - 13:56:21: | Name | Lower bound | Value | Upper bound | Type | INFO - 13:56:21: +------+-------------+-------+-------------+-------+ INFO - 13:56:21: | y_24 | 0.44 | 11.13 | 11.13 | float | INFO - 13:56:21: +------+-------------+-------+-------------+-------+ INFO - 13:56:21: *** End MDOScenario execution (time: 0:00:00.023953) *** {'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:`.MLRegressionAlgo` 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:`.MLRegressionAlgo` 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.140 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 `_