Source code for gemseo.disciplines.constraint_aggregation

# Copyright 2021 IRT Saint Exupéry,
# 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
# 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.
# Contributors:
#    INITIAL AUTHORS - API and implementation and/or documentation
#       :author: Francois Gallard
"""An MDODiscipline to aggregate constraints."""
from __future__ import annotations

from typing import Any
from typing import Callable
from typing import Final
from typing import Sequence

from numpy import atleast_1d
from strenum import StrEnum

from gemseo.algos.aggregation.core import compute_iks_agg
from gemseo.algos.aggregation.core import compute_ks_agg
from gemseo.algos.aggregation.core import compute_max_agg
from gemseo.algos.aggregation.core import compute_max_agg_jac
from gemseo.algos.aggregation.core import compute_partial_iks_agg_jac
from gemseo.algos.aggregation.core import compute_partial_ks_agg_jac
from gemseo.algos.aggregation.core import compute_partial_sum_positive_square_agg_jac
from gemseo.algos.aggregation.core import compute_partial_sum_square_agg_jac
from gemseo.algos.aggregation.core import compute_sum_positive_square_agg
from gemseo.algos.aggregation.core import compute_sum_square_agg
from gemseo.core.discipline import MDODiscipline
from gemseo.utils.data_conversion import concatenate_dict_of_arrays_to_array
from gemseo.utils.data_conversion import split_array_to_dict_of_arrays

[docs]class ConstraintAggregation(MDODiscipline): """A discipline that aggregates the constraints computed by other disciplines. An efficient alternative to constraint aggregation in the optimization problem is to aggregate the constraint in a discipline. This can be included in a MDO formulation, and in particular in an MDA, so only one adjoint calculation can be performed for the aggregated constraint instead of one adjoint per original constraint dimension. See :cite:`kennedy2015improved` and :cite:`kreisselmeier1983application`. """
[docs] class EvaluationFunction(StrEnum): """A function to compute an aggregation of constraints.""" IKS = "IKS" """The induces exponential function.""" KS = "KS" """The Kreisselmeier–Steinhauser function.""" POS_SUM = "POS_SUM" """The positive sum squared function.""" MAX = "MAX" """The maximum function.""" SUM = "SUM" """The sum squared function."""
_EVALUATION_FUNCTION_MAP: Final[EvaluationFunction, Callable] = { EvaluationFunction.IKS: compute_iks_agg, EvaluationFunction.KS: compute_ks_agg, EvaluationFunction.POS_SUM: compute_sum_positive_square_agg, EvaluationFunction.MAX: compute_max_agg, EvaluationFunction.SUM: compute_sum_square_agg, } _JACOBIAN_EVALUATION_FUNCTION_MAP: Final[EvaluationFunction, Callable] = { EvaluationFunction.IKS: compute_partial_iks_agg_jac, EvaluationFunction.KS: compute_partial_ks_agg_jac, EvaluationFunction.POS_SUM: compute_partial_sum_positive_square_agg_jac, EvaluationFunction.MAX: compute_max_agg_jac, EvaluationFunction.SUM: compute_partial_sum_square_agg_jac, } def __init__( self, constraint_names: Sequence[str], aggregation_function: EvaluationFunction, name: str | None = None, **options: Any, ) -> None: """ Args: constraint_names: The names of the constraints to aggregate, which must be discipline outputs. aggregation_function: The aggregation function or its name, e.g. IKS, KS, POS_SUM and SUM. name: The name of the discipline. **options: The options for the aggregation method. Raises: ValueError: If the method is not supported. """ # noqa: D205, D212, D415 super().__init__(name) self.__method_name = aggregation_function self.__meth_options = options self.input_grammar.update_from_names(constraint_names) self.output_grammar.update_from_names( [ f"{aggregation_function}_{constraint_name}" for constraint_name in constraint_names ] ) self.__data_sizes = {} def _run(self) -> None: input_data = concatenate_dict_of_arrays_to_array( self.local_data, self.get_input_data_names() ) evaluation_function = self._EVALUATION_FUNCTION_MAP[self.__method_name] output_data = atleast_1d(evaluation_function(input_data, **self.__meth_options)) output_names = self.get_output_data_names() output_names_to_output_values = split_array_to_dict_of_arrays( output_data, dict.fromkeys(output_names, 1), output_names, ) self.store_local_data(**output_names_to_output_values) if not self.__data_sizes: self.__data_sizes = { variable_name: variable_value.size for variable_name, variable_value in self.local_data.items() } def _compute_jacobian( self, inputs: Sequence[str] | None = None, outputs: Sequence[str] | None = None ) -> None: input_names = self.get_input_data_names() evaluation_function = self._JACOBIAN_EVALUATION_FUNCTION_MAP[self.__method_name] self.jac = split_array_to_dict_of_arrays( evaluation_function( concatenate_dict_of_arrays_to_array(self.local_data, input_names), **self.__meth_options, ), self.__data_sizes, self.get_output_data_names(), input_names, )