import numpy as np
from .function import OptFunction
[docs]
class OptFunctionSubset(OptFunction):
    """
    A function composed of a subset of a function's
    components.
    Attributes
    ----------
    func_org: iwopy.OptFunction
        The original function
    subset: list of int
        The component choice
    :group: core
    """
[docs]
    def __init__(self, function, subset, name=None):
        """
        Constructor
        Parameters
        ----------
        function: iwopy.OptFunction
            The original function
        subset: list of int
            The component choice
        name: str, optional
            The function name
        """
        if name is None:
            name = f"{function.name}[" + ",".join([str(i) for i in subset]) + "]"
        super().__init__(function.problem, name)
        self.func_org = function
        self.subset = subset 
[docs]
    def initialize(self, verbosity=0):
        """
        Initialize the object.
        Parameters
        ----------
        verbosity: int
            The verbosity level, 0 = silent
        """
        f = self.func_org
        if not f.initialized:
            f.initialize(verbosity)
        self._cnames = [f._cnames[i] for i in self.subset]
        self._vdepsi = f.vardeps_int()[self.subset]
        self._vdepsf = f.vardeps_float()[self.subset]
        self._vnamesi = [f._vnamesi[i] for i in np.unique(self._vdepsi)]
        self._vnamesf = [f._vnamesf[i] for i in np.unique(self._vdepsf)]
        super().initialize(verbosity) 
[docs]
    def vardeps_int(self):
        """
        Gets the dependencies of all components
        on the function int variables
        Returns
        -------
        deps: numpy.ndarray of bool
            The dependencies of components on function
            variables, shape: (n_components, n_vars_int)
        """
        return self._vdepsi 
[docs]
    def vardeps_float(self):
        """
        Gets the dependencies of all components
        on the function float variables
        Returns
        -------
        deps: numpy.ndarray of bool
            The dependencies of components on function
            variables, shape: (n_components, n_vars_float)
        """
        return self._vdepsf 
[docs]
    def n_components(self):
        """
        Returns the number of components of the
        function.
        Returns
        -------
        int:
            The number of components.
        """
        return len(self.subset) 
[docs]
    def calc_individual(self, vars_int, vars_float, problem_results, components=None):
        """
        Calculate values for a single individual of the
        underlying problem.
        Parameters
        ----------
        vars_int: np.array
            The integer variable values, shape: (n_vars_int,)
        vars_float: np.array
            The float variable values, shape: (n_vars_float,)
        problem_results: Any
            The results of the variable application
            to the problem
        components: list of int, optional
            The selected components or None for all
        Returns
        -------
        values: np.array
            The component values, shape: (n_sel_components,)
        """
        cmpts = (
            self.subset if components is None else [self.subset[i] for i in components]
        )
        return self.func_org.calc_individual(
            vars_int, vars_float, problem_results, cmpts
        ) 
[docs]
    def calc_population(self, vars_int, vars_float, problem_results, components=None):
        """
        Calculate values for all individuals of a population.
        Parameters
        ----------
        vars_int: np.array
            The integer variable values, shape: (n_pop, n_vars_int)
        vars_float: np.array
            The float variable values, shape: (n_pop, n_vars_float)
        problem_results: Any
            The results of the variable application
            to the problem
        components: list of int, optional
            The selected components or None for all
        Returns
        -------
        values: np.array
            The component values, shape: (n_pop, n_sel_components,)
        """
        cmpts = (
            self.subset if components is None else [self.subset[i] for i in components]
        )
        return self.func_org.calc_population(
            vars_int, vars_float, problem_results, cmpts
        ) 
[docs]
    def ana_deriv(self, vars_int, vars_float, var, components=None):
        """
        Calculates the analytic derivative, if possible.
        Use `numpy.nan` if analytic derivatives cannot be calculated.
        Parameters
        ----------
        vars_int: np.array
            The integer variable values, shape: (n_vars_int,)
        vars_float: np.array
            The float variable values, shape: (n_vars_float,)
        var: int
            The index of the differentiation float variable
        components: list of int
            The selected components, or None for all
        Returns
        -------
        deriv: numpy.ndarray
            The derivative values, shape: (n_sel_components,)
        """
        cmpts = (
            self.subset if components is None else [self.subset[i] for i in components]
        )
        return self.func_org.ana_deriv(vars_int, vars_float, var, cmpts)