Source code for axisfuzzy.membership.base

#  Copyright (c) yibocat 2025 All Rights Reserved
#  Python: 3.12.7
#  Date: 2025/8/18 18:23
#  Author: yibow
#  Email: yibocat@yeah.net
#  Software: AxisFuzzy

"""
Abstract base class for membership functions in AxisFuzzy.

This module defines the core interface and behavior that all membership functions
must implement. Membership functions are mathematical functions that map crisp
values to membership degrees in the range [0, 1], forming the foundation of
fuzzy set theory and the AxisFuzzy fuzzification system.

The base class provides:
- Abstract interface specification for all membership function implementations
- Parameter management and introspection capabilities
- Function object behavior through ``__call__`` method
- Built-in visualization support for function plotting
- Standardized metadata handling

Architecture
------------
The membership function system follows a template method pattern where:

1. **Interface Definition**: :class:`MembershipFunction` defines the required
   methods that all concrete implementations must provide.

2. **Parameter Management**: Built-in support for storing, retrieving, and
   updating function parameters with automatic metadata synchronization.

3. **Callable Interface**: All membership functions can be invoked directly
   as callable objects, providing a natural mathematical syntax.

4. **Visualization Support**: Optional plotting capabilities for visual
   inspection and validation of membership function shapes.

Core Methods
------------
All membership function implementations must provide:

- ``compute(x)``: The primary computation method that calculates membership
  degrees for input values. Accepts both scalar and array inputs.
- ``set_parameters(**kwargs)``: Method for updating function parameters
  after instantiation with validation and metadata synchronization.

Additional inherited capabilities include:

- ``get_parameters()``: Returns current parameter dictionary for introspection
- ``__call__(x)``: Enables direct function invocation syntax
- ``plot(x_range, num_points)``: Generates matplotlib visualizations

Usage Patterns
---------------
Membership functions are typically used in three contexts:

1. **Fuzzification**: Converting crisp values to fuzzy membership degrees
   during the construction of :class:`Fuzznum` objects.

2. **Rule Systems**: Defining linguistic variables and fuzzy rules in
   expert systems and control applications.

3. **Data Analysis**: Analyzing uncertainty and partial membership in
   datasets with imprecise or subjective classifications.

Design Principles
-----------------
The membership function design follows several key principles:

- **Mathematical Correctness**: All functions guarantee output in [0, 1]
- **Numerical Stability**: Robust handling of edge cases and numerical errors
- **Performance**: Efficient vectorized computation using NumPy arrays
- **Extensibility**: Easy to subclass for custom membership functions
- **Consistency**: Uniform parameter naming and behavior across all implementations

Notes
-----
- All membership functions are stateful objects that store their parameters
- Thread safety depends on the specific implementation but is not guaranteed by the base class
- Parameter validation is delegated to concrete implementations
- Visualization requires matplotlib and is optional for core functionality

See Also
--------
axisfuzzy.membership.function : Concrete implementations of standard membership functions
axisfuzzy.membership.factory : Factory functions for creating membership function instances
axisfuzzy.fuzzify : Fuzzification system that uses membership functions

Examples
--------
Creating and using a custom membership function:

.. code-block:: python

    import numpy as np
    from axisfuzzy.membership.base import MembershipFunction

    class LinearMF(MembershipFunction):
        def __init__(self, slope=1.0, intercept=0.0):
            super().__init__()
            self.slope = slope
            self.intercept = intercept
            self.parameters = {'slope': slope, 'intercept': intercept}

        def compute(self, x):
            result = self.slope * x + self.intercept
            return np.clip(result, 0.0, 1.0)  # Ensure [0,1] range

        def set_parameters(self, **kwargs):
            if 'slope' in kwargs:
                self.slope = kwargs['slope']
                self.parameters['slope'] = self.slope
            if 'intercept' in kwargs:
                self.intercept = kwargs['intercept']
                self.parameters['intercept'] = self.intercept

    # Usage
    mf = LinearMF(slope=0.5, intercept=0.1)
    x = np.array([0, 0.5, 1.0, 2.0])
    membership = mf(x)  # Calls compute() via __call__
    print(membership)   # [0.1, 0.35, 0.6, 1.0]

Batch processing with arrays:

.. code-block:: python

    # Process multiple values efficiently
    x_values = np.linspace(0, 10, 100)
    membership_degrees = mf.compute(x_values)

    # Visualize the function
    mf.plot(x_range=(0, 10), num_points=200)

Parameter management:

.. code-block:: python

    # Inspect current parameters
    params = mf.get_parameters()
    print(params)  # {'slope': 0.5, 'intercept': 0.1}

    # Update parameters dynamically
    mf.set_parameters(slope=1.0, intercept=0.0)
    new_params = mf.get_parameters()
    print(new_params)  # {'slope': 1.0, 'intercept': 0.0}

References
----------
- Zadeh, L.A. (1965). "Fuzzy sets". Information and Control, 8(3), 338-353.
- Klir, G.J. & Yuan, B. (1995). "Fuzzy Sets and Fuzzy Logic: Theory and Applications"
- Ross, T.J. (2010). "Fuzzy Logic with Engineering Applications"
"""

from abc import ABC, abstractmethod
from typing import Union, Tuple

import numpy as np


[docs] class MembershipFunction(ABC): """ Abstract base class for all membership functions in AxisFuzzy. This class defines the interface and common behavior that all membership function implementations must provide. It serves as the foundation for the AxisFuzzy fuzzification system and fuzzy set operations. Membership functions map input values to membership degrees in the range [0, 1], providing the mathematical foundation for fuzzy logic operations and fuzzy set theory applications. Attributes ---------- name : str Human-readable name of the membership function, typically the class name. parameters : dict Dictionary storing the current parameter values of the function. Automatically populated by concrete implementations. Notes ----- All concrete implementations must override both :meth:`compute` and :meth:`set_parameters` methods. The base class provides parameter storage, callable interface, and optional visualization capabilities. Thread safety is not guaranteed and depends on the specific implementation. Most implementations are read-only after construction but may support parameter updates through :meth:`set_parameters`. See Also -------- axisfuzzy.membership.function : Standard membership function implementations axisfuzzy.membership.factory : Factory functions for creating instances Examples -------- Basic usage pattern for subclassing: .. code-block:: python class CustomMF(MembershipFunction): def __init__(self, param1, param2=1.0): super().__init__() self.param1 = param1 self.param2 = param2 self.parameters = {'param1': param1, 'param2': param2} def compute(self, x): # Implementation specific logic return np.clip(some_function(x, self.param1, self.param2), 0, 1) def set_parameters(self, **kwargs): if 'param1' in kwargs: self.param1 = kwargs['param1'] self.parameters['param1'] = self.param1 # ... handle other parameters """ def __init__(self, *args, **kwargs): """ Initialize the base membership function. Sets up the basic attributes that all membership functions share, including the function name and parameter storage dictionary. Parameters ---------- *args, **kwargs Arguments passed to concrete implementations. The base class ignores all arguments but concrete classes may use them for parameter initialization. Notes ----- Concrete implementations should call ``super().__init__()`` before setting their specific parameters and updating the ``parameters`` dict. """ self.name = self.__class__.__name__ self.parameters = {}
[docs] @abstractmethod def compute(self, x: Union[float, np.ndarray]) -> Union[float, np.ndarray]: """ Compute membership degrees for input values. This is the core method that all membership functions must implement. It transforms input values into membership degrees in the range [0, 1]. Parameters ---------- x : float or numpy.ndarray Input value(s) for which to compute membership degrees. Can be a scalar or array of any shape. Returns ------- float or numpy.ndarray Membership degree(s) corresponding to the input values. Output has the same shape as input and values are in [0, 1]. Notes ----- Implementations must ensure that: - All output values are in the range [0, 1] - The function handles both scalar and array inputs correctly - Numerical stability is maintained for edge cases - The function is vectorized for efficient array processing Examples -------- Implementation example for a simple linear membership function: .. code-block:: python def compute(self, x): x = np.asarray(x, dtype=float) result = (x - self.min_val) / (self.max_val - self.min_val) return np.clip(result, 0.0, 1.0) """ pass
[docs] def get_parameters(self) -> dict: """ Retrieve the current parameters of the membership function. Returns a dictionary containing all parameters that define the shape and behavior of the membership function. This is useful for introspection, serialization, and debugging. Returns ------- dict Dictionary mapping parameter names to their current values. The exact keys depend on the specific membership function type. Examples -------- .. code-block:: python # For a triangular membership function mf = TriangularMF(a=0, b=0.5, c=1) params = mf.get_parameters() print(params) # {'a': 0, 'b': 0.5, 'c': 1} # For a Gaussian membership function mf = GaussianMF(sigma=1.0, c=0.0) params = mf.get_parameters() print(params) # {'sigma': 1.0, 'c': 0.0} """ return self.parameters
[docs] @abstractmethod def set_parameters(self, **kwargs) -> None: """ Update the parameters of the membership function. This method allows dynamic modification of function parameters after instantiation. Implementations must validate parameters and update both the internal state and the ``parameters`` dictionary. Parameters ---------- **kwargs Parameter names and values to update. Only parameters recognized by the specific membership function are processed. Raises ------ ValueError If invalid parameter values are provided or if parameter combinations violate mathematical constraints. Notes ----- Implementations should: - Validate all parameter values before updating - Maintain mathematical constraints (e.g., ordering requirements) - Update both internal attributes and the ``parameters`` dict - Provide clear error messages for invalid parameters Examples -------- .. code-block:: python # Update triangular function parameters mf = TriangularMF(a=0, b=0.5, c=1) mf.set_parameters(b=0.6, c=1.2) # Update Gaussian function parameters mf = GaussianMF(sigma=1.0, c=0.0) mf.set_parameters(sigma=1.5) # Only update sigma # Invalid parameter raises ValueError try: mf.set_parameters(sigma=-1.0) # Negative sigma except ValueError as e: print(f"Error: {e}") """ pass
def __call__(self, x: Union[float, np.ndarray]) -> Union[float, np.ndarray]: """ Enable the membership function to be called as a function object. This method provides a convenient interface for computing membership degrees using standard mathematical function call syntax. It delegates to the :meth:`compute` method for the actual calculation. Parameters ---------- x : float or numpy.ndarray Input value(s) for membership degree computation. Returns ------- float or numpy.ndarray Membership degree(s) in the range [0, 1]. Examples -------- .. code-block:: python mf = TriangularMF(a=0, b=0.5, c=1) # Direct function call syntax result = mf(0.3) # Single value results = mf([0.1, 0.5, 0.8]) # Multiple values # Equivalent to calling compute() directly result = mf.compute(0.3) results = mf.compute([0.1, 0.5, 0.8]) """ return self.compute(x)
[docs] def plot(self, x_range: Tuple[float, float] = (0, 1), num_points: int = 1000): """ Generate a matplotlib plot of the membership function. Creates a visual representation of the membership function over the specified input range. This is useful for function validation, parameter tuning, and educational purposes. Parameters ---------- x_range : tuple of float, default (0, 1) The (min, max) range of input values to plot. num_points : int, default 1000 Number of points to use for plotting. Higher values create smoother curves but require more computation. Notes ----- This method requires matplotlib to be installed. If matplotlib is not available, the method will raise an ImportError. The plot shows: - X-axis: Input values over the specified range - Y-axis: Membership degrees from 0 to 1 - Title: Function name and type - Grid: Enabled for easier reading Examples -------- .. code-block:: python # Plot with default settings mf = TriangularMF(a=0, b=0.5, c=1) mf.plot() # Plot over custom range with high resolution mf.plot(x_range=(-2, 3), num_points=2000) # Plot multiple functions for comparison mf1 = TriangularMF(a=0, b=0.3, c=0.6) mf2 = TriangularMF(a=0.4, b=0.7, c=1.0) import matplotlib.pyplot as plt mf1.plot() mf2.plot() plt.legend(['Function 1', 'Function 2']) # For users who want to display the plot immediately. # plt.show() should be called outside the function # to allow for multiple plots to be drawn on the same figure. # plt.show() Raises ------ ImportError If matplotlib is not installed. ValueError If ``x_range`` is invalid or ``num_points`` is not positive. """ import matplotlib.pyplot as plt x = np.linspace(x_range[0], x_range[1], num_points) y = self.compute(x) plt.plot(x, y, label=self.name) plt.xlabel('x') plt.ylabel('Membership Degree') plt.title(f'{self.name} Membership Function') plt.grid(True) # 只有当 name 存在且不以下划线开头时才显示图例 if hasattr(self, 'name') and self.name and not self.name.startswith('_'): plt.legend()
# plt.show()