Source code for pumas.desirability.sigmoid_bell

import math
from functools import partial
from types import ModuleType
from typing import Any, Dict, Optional, Union

from pumas.architecture.exceptions import InvalidBoundaryError
from pumas.desirability.base_models import Desirability
from pumas.desirability.sigmoid import sigmoid
from pumas.uncertainty_management.uncertainties.uncertainties_wrapper import (
    UFloat,
    umath,
)


[docs] def sigmoid_bell( x: Union[float, UFloat], x1: float, x2: float, x3: float, x4: float, k: float = 1.0, base: float = 10.0, invert: bool = False, shift: float = 0.0, math_module: ModuleType = math, ) -> Union[float, UFloat]: """ Calculate the sigmoid bell function value for a given input. This function combines two sigmoid functions to create a bell-shaped curve. Args: x (Union[float, UFloat]): The input value. x1, x2, x3, x4 (float): Shape parameters defining the bell curve. k (float): Slope coefficient. Default is 1.0. base (float): Base of the exponential function. Default is 10.0. invert (bool): Whether to invert the result. Default is False. shift (float): Vertical shift of the curve. Default is 0.0. math_module (ModuleType): Math module to use. Default is math. Returns: Union[float, UFloat]: The calculated sigmoid bell value. Raises: InvalidBoundaryError: If shape parameters are invalid or base <= 1. """ if x3 < x1 or x2 > x4 or x2 < x1 or x4 < x3: raise InvalidBoundaryError( "Invalid shape parameters. Ensure x1 < x2 < x3 < x4." ) if base <= 1: raise InvalidBoundaryError("'base' must be greater than 1") # Combine two sigmoid functions sig1 = sigmoid( x, low=x1, high=x2, k=k, shift=0.0, base=base, math_module=math_module ) sig2 = sigmoid( x, low=x3, high=x4, k=k, shift=0.0, base=base, math_module=math_module ) # Calculate the result result = sig1 - sig2 # type: ignore # invert if needed if invert: result = 1 - result # type: ignore # Apply the shift result = result * (1 - shift) + shift return result
compute_numeric_sigmoid_bell = partial(sigmoid_bell, math_module=math) compute_ufloat_sigmoid_bell = partial(sigmoid_bell, math_module=umath)
[docs] class SigmoidBell(Desirability): name = "sigmoid_bell" """ Sigmoid Bell desirability function implementation. This class implements a sigmoid bell desirability function with adjustable parameters. It provides methods to compute the desirability for both numeric and uncertain float inputs. Mathematical Definition: The sigmoid bell function is defined as the difference of two sigmoid functions: .. math:: D(x) = S_1(x) - S_2(x) where: .. math:: S_1(x) = \\frac{1}{1 + base^{-k \\cdot (x - x_1)/(x_2 - x_1)}} S_2(x) = \\frac{1}{1 + base^{-k \\cdot (x - x_3)/(x_4 - x_3)}} If invert is True, the function becomes: .. math:: D_{inverted}(x) = 1 - D(x) Finally, the shift is applied: .. math:: D_{final}(x) = D(x) \\cdot (1 - shift) + shift Parameters: params (Optional[Dict[str, Any]]): Initial parameters for the sigmoid bell function. Attributes: x1, x2, x3, x4 (float): Shape parameters defining the bell curve. k (float): Slope coefficient, range [1.0, inf), default 1.0. base (float): Base of the exponential function, range (1.0, 10.0], default 10.0. invert (bool): Whether to invert the result, default False. shift (float): Vertical shift of the curve, range [0.0, 1.0], default 0.0. Raises: InvalidBoundaryError: If shape parameters are invalid or base <= 1. InvalidParameterTypeError: If any parameter is of an invalid type. ParameterValueNotSet: If any required parameter is not set. Usage Example: >>> from pumas.desirability import desirability_catalogue >>> desirability_class = desirability_catalogue.get("sigmoid_bell") >>> params = {"x1": 20.0, "x4": 80.0, "x2": 45.0, "x3": 60.0, "k": 1.0, "base": 10.0, "invert": False, "shift": 0.0} >>> desirability = desirability_class(params=params) >>> print(desirability.get_parameters_values()) {'x1': 20.0, 'x2': 45.0, 'x3': 60.0, 'x4': 80.0, 'k': 1.0, 'base': 10.0, 'invert': False, 'shift': 0.0} >>> result = desirability.compute_numeric(x=50.0) >>> print(f"{result:.2f}") 1.00 >>> result = desirability(x=50.0) # Same as compute_numeric >>> print(f"{result:.2f}") 1.00 >>> from uncertainties import ufloat >>> result = desirability.compute_ufloat(x=ufloat(50.0, 20.0)) >>> print(result) 0.9999999+/-0.0000018 """ # noqa: E501 def __init__(self, params: Optional[Dict[str, Any]] = None): """ Initialize the SigmoidBell desirability function. Args: params (Optional[Dict[str, Any]]): Initial parameters for the sigmoid bell function. """ super().__init__() self._set_parameter_definitions( { "x1": { "type": "float", "min": float("-inf"), "max": float("inf"), "default": None, }, "x2": { "type": "float", "min": float("-inf"), "max": float("inf"), "default": None, }, "x3": { "type": "float", "min": float("-inf"), "max": float("inf"), "default": None, }, "x4": { "type": "float", "min": float("-inf"), "max": float("inf"), "default": None, }, "k": {"type": "float", "min": 1.0, "max": None, "default": 1.0}, "base": {"type": "float", "min": 1.0, "max": 10.0, "default": 10.0}, "invert": {"type": "bool", "default": False}, "shift": {"type": "float", "min": 0.0, "max": 1.0, "default": 0.0}, } ) self._validate_and_set_parameters(params)
[docs] def compute_numeric(self, x: float) -> float: """ Compute the sigmoid bell desirability for a numeric input. Args: x (float): The input value. Returns: float: The computed desirability value. Raises: InvalidParameterTypeError: If the input is not a float. ParameterValueNotSet: If any required parameter is not set. """ self._validate_compute_input(x, float) self._check_parameters_values_none() parameters = self.get_parameters_values() return compute_numeric_sigmoid_bell(x, **parameters) # type: ignore
[docs] def compute_ufloat(self, x: UFloat) -> UFloat: """ Compute the sigmoid bell desirability for an uncertain float input. Args: x (UFloat): The uncertain float input value. Returns: UFloat: The computed desirability value with uncertainty. Raises: InvalidParameterTypeError: If the input is not a UFloat. ParameterValueNotSet: If any required parameter is not set. """ self._validate_compute_input(x, UFloat) self._check_parameters_values_none() parameters = self.get_parameters_values() return compute_ufloat_sigmoid_bell(x, **parameters) # type: ignore
__call__ = compute_numeric