Source code for gemseo.core.mdofunctions.make_function

# -*- coding: utf-8 -*-
# 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.

# Contributors:
#    INITIAL AUTHORS - initial API and implementation and/or initial
#                        documentation
#        :author: Francois Gallard, Charlie Vanaret
#    OTHER AUTHORS   - MACROSCOPIC CHANGES
"""Base class to describe a function."""
from __future__ import division, unicode_literals

import logging
from numbers import Number
from typing import TYPE_CHECKING, Callable, Mapping, Sequence, Union

from numpy import hstack, ndarray, reshape, vstack

from gemseo.core.mdofunctions.mdo_function import MDOFunction
from gemseo.utils.data_conversion import DataConversion

if TYPE_CHECKING:
    from gemseo.core.mdofunctions.function_generator import MDOFunctionGenerator


LOGGER = logging.getLogger(__name__)

OperandType = Union[ndarray, Number]
OperatorType = Callable[[OperandType, OperandType], OperandType]


[docs]class MakeFunction(MDOFunction): """A function object from io and reference data.""" def __init__( self, input_names, # type: Sequence[str] output_names, # type: Sequence[str] default_inputs, # type: Mapping[str, ndarray] mdo_function, # type: MDOFunctionGenerator ): # type: (...) -> None """ Args: input_names: The dict keys of the input data. output_names: The dict keys of the output data. default_inputs: The default inputs dict to eventually overload the discipline's default inputs when evaluating the discipline. mdo_function: The MDOFunctionGenerator object. """ self.__input_names = input_names self.__output_names = output_names self.__default_inputs = default_inputs self.__mdo_function = mdo_function default_name = "_".join(self.__output_names) super(MakeFunction, self).__init__( self._func, jac=self._func_jac, name=default_name, args=self.__input_names, outvars=self.__output_names, ) def _func( self, x_vect # type: ndarray ): # type: (...) -> ndarray """A function which executes a discipline. Args: x_vect: The input vector of the function. Returns: The selected outputs of the discipline. """ for name in self.__input_names: if name not in self.__mdo_function.discipline.default_inputs: raise ValueError( "Discipline {}" " has no default_input named {}" ", while input is required" " by MDOFunction.".format(self.__mdo_function.discipline.name, name) ) defaults = self.__mdo_function.discipline.default_inputs if self.__default_inputs is not None: defaults.update(self.__default_inputs) data = DataConversion.update_dict_from_array( defaults, self.__input_names, x_vect ) self.__mdo_function.discipline.reset_statuses_for_run() computed_values = self.__mdo_function.discipline.execute(data) values_array = DataConversion.dict_to_array( computed_values, self.__output_names ) if values_array.size == 1: # Then the function is scalar return values_array[0] return values_array def _func_jac( self, x_vect # type: ndarray ): # type: (...) -> ndarray """A function which linearizes a discipline. Args: x_vect: The input vector of the function. Returns: The selected outputs of the discipline. """ defaults = self.__mdo_function.discipline.default_inputs n_dv = len(x_vect) data = DataConversion.update_dict_from_array( defaults, self.__input_names, x_vect ) self.__mdo_function.discipline.linearize(data) grad_array = [] for out_name in self.__output_names: jac_loc = self.__mdo_function.discipline.jac[out_name] grad_loc = DataConversion.dict_to_array(jac_loc, self.__input_names) grad_output = hstack(grad_loc) if len(grad_output) > n_dv: grad_output = reshape(grad_output, (grad_output.size // n_dv, n_dv)) grad_array.append(grad_output) grad = vstack(grad_array).real if grad.shape[0] == 1: grad = grad.flatten() assert len(x_vect) == len(grad) return grad