Source code for gemseo_umdo.formulations.sampling

# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
# Copyright 2022 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
r"""Sampling for multidisciplinary design problems under uncertainty.

:class:`~gemseo_umdo.formulations.sampling.Sampling` is an
:class:`~gemseo_umdo.formulations.formulation.UMDOFormulation`
estimating the statistics with (quasi) Monte Carlo techniques.

E.g.
:math:`\mathbb{E}[f(x,U)] \approx \frac{1}{N}\sum_{i=1}^N f\left(x,U^{(i)}\right)`
or
:math:`\mathbb{V}[f(x,U)] \approx
\frac{1}{N}\sum_{i=1}^N \left(f\left(x,U^{(i)}\right)-
\frac{1}{N}\sum_{j=1}^N f\left(x,U^{(j)}\right)\right)^2`
where :math:`U` is normally distributed
with mean :math:`\mu` and unit variance :math:`\sigma`
and :math:`U^{(1)},\ldots,U^{(1)}` are :math:`N` realizations of :math:`U`
obtained with an optimized Latin hypercube sampling technique.
"""
from __future__ import annotations

import logging
from typing import Any
from typing import ClassVar
from typing import Mapping
from typing import Sequence

from gemseo.algos.design_space import DesignSpace
from gemseo.algos.doe.doe_factory import DOEFactory
from gemseo.algos.opt_problem import OptimizationProblem
from gemseo.algos.parameter_space import ParameterSpace
from gemseo.core.discipline import MDODiscipline
from gemseo.core.formulation import MDOFormulation
from gemseo.utils.logging_tools import LoggingContext
from numpy import ndarray

from gemseo_umdo.estimators.sampling import SamplingEstimatorFactory
from gemseo_umdo.formulations.formulation import UMDOFormulation

LOGGER = logging.getLogger(__name__)


[docs]class Sampling(UMDOFormulation): """Sampling-based robust MDO formulation.""" processed_functions: list[str] """The names of the functions whose statistics have been estimated.""" _STATISTIC_FACTORY: ClassVar = SamplingEstimatorFactory() def __init__( self, disciplines: Sequence[MDODiscipline], objective_name: str, design_space: DesignSpace, mdo_formulation: MDOFormulation, uncertain_space: ParameterSpace, objective_statistic_name: str, n_samples: int, objective_statistic_parameters: Mapping[str, Any] | None = None, maximize_objective: bool = False, grammar_type: str = MDODiscipline.JSON_GRAMMAR_TYPE, algo: str = "OT_OPT_LHS", algo_options: Mapping[str, Any] | None = None, seed: int = 1, **options: Any, ) -> None: """# noqa: D205 D212 D415 Args: n_samples: The number of samples, i.e. the size of the DOE. algo: The name of the DOE algorithm. algo_options: The options of the DOE algorithm. """ self.__doe_algo = DOEFactory().create(algo) self.__doe_algo_options = algo_options or {} self.__doe_algo_options["n_samples"] = n_samples self.__n_samples = n_samples self.processed_functions = [] self.__seed = seed super().__init__( disciplines, objective_name, design_space, mdo_formulation, uncertain_space, objective_statistic_name, objective_statistic_options=objective_statistic_parameters, maximize_objective=maximize_objective, grammar_type=grammar_type, **options, ) mdo_formulation = self._mdo_formulation.__class__.__name__ formulation = self.__class__.__name__ self.name = f"{formulation}[{mdo_formulation}; {algo}({n_samples})]" @property def _n_samples(self) -> int: """The number of samples.""" return self.__n_samples @property def _algo(self) -> str: """The name of the DOE algorithm.""" return self.__doe_algo
[docs] def compute_samples(self, problem: OptimizationProblem) -> None: """Evaluate the functions of a problem with a DOE algorithm. Args: problem: The problem. """ with LoggingContext(LOGGER, logging.WARNING): self.__doe_algo.seed = self.__seed self.__doe_algo.execute( problem, seed=self.__seed, **self.__doe_algo_options )
class _StatisticFunction(UMDOFormulation._StatisticFunction): def _func(self, input_data: ndarray) -> ndarray: formulation = self._formulation problem = formulation.mdo_formulation.opt_problem if self._function_name in formulation.processed_functions: formulation.processed_functions = [] problem.reset() database = problem.database if not database: formulation.update_top_level_disciplines(input_data) formulation.compute_samples(problem) formulation.processed_functions.append(self._function_name) samples, _, _ = database.get_history_array( [self._function_name], add_dv=False ) return self._estimate_statistic(samples, **self._statistic_parameters)