Source code for pumas.desirability.bell

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

from pumas.desirability.base_models import Desirability
from pumas.uncertainty_management.uncertainties.uncertainties_wrapper import (
    UFloat,
    umath,
)


[docs] def bell( x: Union[float, UFloat], width: float, slope: float, center: float, invert: bool = False, shift: float = 0.0, math_module: ModuleType = math, ) -> Union[float, UFloat]: exponent = 2 * abs(slope) base = abs((x - center) / width) # type: ignore if base > 1 and exponent > math_module.log(sys.float_info.max) / math_module.log( base ): return shift result = 1 / (1 + base**exponent) # invert if needed if invert: result = 1 - result # Apply the shift result = result * (1 - shift) + shift return result # type: ignore
compute_numeric_bell = partial(bell, math_module=math) compute_ufloat_bell = partial(bell, math_module=umath) def get_bell_inflection_points( center: float, width: float, slope: float ) -> tuple[float, float]: """ Calculate the inflection points of a generalized bell membership function. Args: center (float): The center of the bell curve. width (float): The width parameter of the bell curve. slope (float): The slope parameter of the bell curve. Returns: tuple: A tuple containing the two inflection points (left, right). """ if width <= 0 or slope <= 0: raise ValueError("Width and slope must be positive.") # Calculate the distance from the center to each inflection point distance = width * (2 ** (1 / (2 * slope)) - 1) ** (1 / (2 * slope)) # Calculate the two inflection points left_inflection = center - distance right_inflection = center + distance return left_inflection, right_inflection def get_bell_slope_pivot_points( center: float, width: float, slope: float ) -> tuple[float, float]: """ Calculate the pivot points of a generalized bell membership function. The values of these points do not change when varying the slope parameter. Args: center (float): The center of the bell curve. width (float): The width parameter of the bell curve. slope (float): The slope parameter of the bell curve. Returns: tuple: A tuple containing the two pivot points (left, right). """ if width <= 0 or slope <= 0: raise ValueError("Width and slope must be positive.") # Calculate the two pivot points left_pivot = center - width right_pivot = center + width return left_pivot, right_pivot
[docs] class Bell(Desirability): name = "bell" """ Bell desirability function implementation. Mathematical Definition: The bell function is defined as: .. math:: f(x) = \\frac{1}{1 + |\\frac{x - center}{width}|^{2 * slope}} * (1 - shift) + shift Where: * `x` is the input value. * `width` is the width parameter of the bell curve, controls the horizontal spread (width > 0). * `slope` is the slope parameter of the bell curve, controls the steepness of the curve's sides. * `center` is the center of the bell curve, indicates the `x` value at the highest point of the curve (peak). * `invert` is a boolean parameter that, if True, inverts the curve. * `shift` is the vertical shift applied to the entire curve, ranging from 0 (no shift) to 1 (maximum shift). Parameters: params (Optional[Dict[str, Any]]): Initial parameters for the sigmoid function. Defaults to None. Attributes: width (float): The width parameter of the bell curve, controls the horizontal spread (width > 0). slope (float): The slope parameter of the bell curve, controls the steepness of the curve's sides. center (float): The center of the bell curve, indicates the `x` value at the highest point of the curve (peak). invert (bool): If True, the curve is inverted, i.e., the desirability decreases as `x` approaches the center. shift (float): The vertical shift applied to the entire curve, ranging from 0 (no shift) to 1 (maximum shift). Usage Example: >>> from pumas.desirability import desirability_catalogue >>> desirability_class = desirability_catalogue.get("bell") >>> params = {'center': 0.5, 'width': 1.0, 'slope': 2.0, 'invert': False, 'shift': 0.0} >>> desirability = desirability_class(params=params) >>> print(desirability.get_parameters_values()) {'center': 0.5, 'width': 1.0, 'slope': 2.0, 'invert': False, 'shift': 0.0} >>> result = desirability.compute_numeric(x=0.5) >>> print(f"{result:.2f}") 1.00 >>> result = desirability(x=0.5) # Same as compute_numeric >>> print(f"{result:.2f}") 1.00 >>> from uncertainties import ufloat >>> result = desirability.compute_ufloat(x=ufloat(0.5, 0.1)) >>> print(result) 1.0+/-0 """ # noqa: E501 def __init__(self, params: Optional[Dict[str, Any]] = None): """ Initialize the Bell desirability function. Args: params (Optional[Dict[str, Any]]): Initial parameters for the sigmoid function. """ # noqa: E501 super().__init__() self._set_parameter_definitions( { "center": { "type": "float", "min": float("-inf"), "max": float("inf"), "default": None, }, "width": { "type": "float", "min": sys.float_info.epsilon, "max": float("inf"), "default": None, }, "slope": { "type": "float", "min": 0.0, "max": float("inf"), "default": 1.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: Union[int, float]) -> float: """ Compute the bell desirability for a numeric input. Args: x (Union[int, float]): The numeric 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(item=x, expected_type=(int, float)) self._check_parameters_values_none() parameters = self.get_parameters_values() return compute_numeric_bell(x, **parameters) # type: ignore
[docs] def compute_ufloat(self, x: UFloat) -> UFloat: """ Compute the 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_bell(x, **parameters) # type: ignore
__call__ = compute_numeric