Skip to content

Memberships

Differentiable membership functions for fuzzy TSK models.

This module defines learnable membership function classes for highFIS. All membership functions inherit from MembershipFunction, which itself inherits from torch.nn.Module.

Membership functions

Gaussian-based - GaussianMF — standard Gaussian with mean and sigma. - CompositeGaussianMF — Gaussian with a positive lower bound to avoid zero membership. - GaussianPIMF — Gaussian with a positive infimum, useful for softmin-stable models.

Exponential - CompositeExponentialMF — CEMF with lower bound 1/k, used by AYATSK.

Piecewise polynomial - TriangularMF — triangular membership with left/center/right. - TrapezoidalMF — trapezoidal membership with four vertices. - PiMF — pi-shaped membership with smooth S/Z transitions. - SShapedMF / ZShapedMF — smooth S/Z membership curves. - LinSShapedMF / LinZShapedMF — linear S/Z membership curves.

Sigmoidal - SigmoidalMF — standard sigmoid. - DiffSigmoidalMF — difference of two sigmoids. - ProdSigmoidalMF — product of two sigmoids.

Notes
  • Membership parameters are trainable and differentiable.
  • This module is intended for use with TSK models in highfis.models.

BellMF

Bases: MembershipFunction

Generalized bell membership: 1 / (1 + |((x-c)/a)|^(2b)).

Initialize bell MF with width a, slope b, and center.

Source code in highfis/memberships.py
def __init__(self, a: float = 1.0, b: float = 2.0, center: float = 0.0, eps: float | None = None) -> None:
    """Initialize bell MF with width *a*, slope *b*, and *center*."""
    super().__init__(eps=eps)
    if a <= 0:
        raise ValueError("a must be positive")
    if b <= 0:
        raise ValueError("b must be positive")
    self.raw_a = nn.Parameter(torch.tensor(_inv_softplus(float(a), eps)))
    self.raw_b = nn.Parameter(torch.tensor(_inv_softplus(float(b), eps)))
    self.center = nn.Parameter(torch.tensor(float(center)))

a property

Return positive width via softplus reparameterization.

b property

Return positive slope via softplus reparameterization.

forward

Compute bell membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute bell membership values for input tensor."""
    x = self._as_tensor(x)
    return 1.0 / (1.0 + torch.abs((x - self.center) / self.a).pow(2.0 * self.b))

CompositeExponentialMF

Bases: MembershipFunction

Composite exponential membership function used in AYATSK.

Initialize composite exponential membership function.

Parameters:

Name Type Description Default
center float

Center of the membership function \(c\).

0.0
sigma float

Width \(\sigma > 0\). Softplus-reparameterized to remain positive during training.

1.0
k float

Lower-bound control parameter \(k > 1\). The membership lower bound equals 1 / k.

10.0
eps float | None

Numeric stability constant. None uses :func:torch.finfo machine epsilon.

None

Raises:

Type Description
ValueError

If sigma is not positive or k is not > 1.

Source code in highfis/memberships.py
def __init__(
    self,
    center: float = 0.0,
    sigma: float = 1.0,
    k: float = 10.0,
    eps: float | None = None,
) -> None:
    r"""Initialize composite exponential membership function.

    Args:
        center: Center of the membership function $c$.
        sigma: Width $\sigma > 0$.  Softplus-reparameterized to
            remain positive during training.
        k: Lower-bound control parameter $k > 1$.  The membership
            lower bound equals ``1 / k``.
        eps: Numeric stability constant.  ``None`` uses
            :func:`torch.finfo` machine epsilon.

    Raises:
        ValueError: If *sigma* is not positive or *k* is not > 1.
    """
    super().__init__(eps=eps)
    if sigma <= 0:
        raise ValueError("sigma must be positive")
    if k <= 1.0:
        raise ValueError("k must be greater than 1")
    self.center = nn.Parameter(torch.tensor(float(center)))
    self.raw_sigma = nn.Parameter(torch.tensor(_inv_softplus(float(sigma), eps)))
    self.k = float(k)

sigma property

Return positive sigma using softplus reparameterization.

forward

Compute CompositeExponentialMF membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute CompositeExponentialMF membership values for input tensor."""
    x = self._as_tensor(x)
    exponent = -0.5 * ((x - self.center) / self.sigma).square()
    return torch.pow(self.k, -1.0 + torch.exp(exponent))

CompositeGaussianMF

Bases: MembershipFunction

Composite Gaussian membership with a nonzero lower bound.

Initialize composite Gaussian membership function.

Parameters:

Name Type Description Default
mean float

Center of the Gaussian \(c\).

0.0
sigma float

Width \(\sigma > 0\). Softplus-reparameterized to remain positive during training.

1.0
eps float | None

Numeric stability constant and lower bound of the membership value. None uses :func:torch.finfo machine epsilon.

None

Raises:

Type Description
ValueError

If sigma is not positive.

Source code in highfis/memberships.py
def __init__(self, mean: float = 0.0, sigma: float = 1.0, eps: float | None = None) -> None:
    r"""Initialize composite Gaussian membership function.

    Args:
        mean: Center of the Gaussian $c$.
        sigma: Width $\sigma > 0$.  Softplus-reparameterized to
            remain positive during training.
        eps: Numeric stability constant and lower bound of the
            membership value.  ``None`` uses :func:`torch.finfo`
            machine epsilon.

    Raises:
        ValueError: If *sigma* is not positive.
    """
    super().__init__(eps=eps)
    if sigma <= 0:
        raise ValueError("sigma must be positive")
    self.mean = nn.Parameter(torch.tensor(float(mean)))
    self.raw_sigma = nn.Parameter(torch.tensor(_inv_softplus(float(sigma), eps)))

sigma property

Return positive sigma using softplus reparameterization.

forward

Compute Composite Gaussian membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute Composite Gaussian membership values for input tensor."""
    x = self._as_tensor(x)
    z = (x - self.mean) / self.sigma
    return self.eps + (1.0 - self.eps) * torch.exp(-0.5 * z.square())

GaussianMF

Bases: MembershipFunction

Gaussian membership: exp(-((x-c)^2)/(2*sigma^2)).

Initialize Gaussian membership function.

Parameters:

Name Type Description Default
mean float

Center of the Gaussian \(c\).

0.0
sigma float

Width \(\sigma > 0\). Softplus-reparameterized to remain positive during training.

1.0
eps float | None

Numeric stability constant. None uses :func:torch.finfo machine epsilon.

None

Raises:

Type Description
ValueError

If sigma is not positive.

Source code in highfis/memberships.py
def __init__(self, mean: float = 0.0, sigma: float = 1.0, eps: float | None = None) -> None:
    r"""Initialize Gaussian membership function.

    Args:
        mean: Center of the Gaussian $c$.
        sigma: Width $\sigma > 0$.  Softplus-reparameterized to
            remain positive during training.
        eps: Numeric stability constant.  ``None`` uses
            :func:`torch.finfo` machine epsilon.

    Raises:
        ValueError: If *sigma* is not positive.
    """
    super().__init__(eps=eps)
    if sigma <= 0:
        raise ValueError("sigma must be positive")
    self.mean = nn.Parameter(torch.tensor(float(mean)))
    self.raw_sigma = nn.Parameter(torch.tensor(_inv_softplus(float(sigma), eps)))

sigma property

Return positive sigma using softplus reparameterization.

forward

Compute Gaussian membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute Gaussian membership values for input tensor."""
    x = self._as_tensor(x)
    z = (x - self.mean) / self.sigma
    return torch.exp(-0.5 * z.square())

GaussianPIMF

Bases: MembershipFunction

Gaussian membership with a positive infimum (PIMF).

Defined as exp(-K * (1 - exp(-(x - m)^2 / (2 * sigma^2)))). The infimum of this function is exp(-K) > 0, which prevents numeric underflow when used with softmin-based T-norms on high-dimensional data.

Reference: Ma et al., "An adaptive double-parameter softmin based Takagi-Sugeno-Kang fuzzy system for high-dimensional data", Fuzzy Sets and Systems 521 (2025), Eq. (41).

Initialize Gaussian PIMF with center, spread, and infimum control K.

Parameters:

Name Type Description Default
mean float

Center of the Gaussian.

0.0
sigma float

Spread (standard deviation); must be positive.

1.0
K float

Positive infimum control parameter in (0, 745]. The membership infimum is exp(-K).

1.0
eps float | None

Numerical stability constant.

None
Source code in highfis/memberships.py
def __init__(
    self,
    mean: float = 0.0,
    sigma: float = 1.0,
    K: float = 1.0,
    eps: float | None = None,
) -> None:
    """Initialize Gaussian PIMF with center, spread, and infimum control K.

    Args:
        mean: Center of the Gaussian.
        sigma: Spread (standard deviation); must be positive.
        K: Positive infimum control parameter in (0, 745].
            The membership infimum is exp(-K).
        eps: Numerical stability constant.
    """
    super().__init__(eps=eps)
    if sigma <= 0:
        raise ValueError("sigma must be positive")
    if not (0.0 < K <= 745.0):
        raise ValueError("K must be in the interval (0, 745]")
    self.mean = nn.Parameter(torch.tensor(float(mean)))
    self.raw_sigma = nn.Parameter(torch.tensor(_inv_softplus(float(sigma), eps)))
    self.K = float(K)

sigma property

Return the positive standard deviation via softplus reparameterization.

forward

Compute Gaussian PIMF membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute Gaussian PIMF membership values for input tensor."""
    x = self._as_tensor(x)
    z_sq = ((x - self.mean) / self.sigma).square()
    inner = 1.0 - torch.exp(-0.5 * z_sq)
    return torch.exp(-self.K * inner)

LinSShapedMF

Bases: MembershipFunction

Linear S-shaped membership from 0 to 1 between a and b.

Initialize with ramp start (a) and end (b); requires a < b.

Source code in highfis/memberships.py
def __init__(self, a: float, b: float, eps: float | None = None) -> None:
    """Initialize with ramp start (a) and end (b); requires a < b."""
    super().__init__(eps=eps)
    if not (a < b):
        raise ValueError("expected a < b")
    self.a = nn.Parameter(torch.tensor(float(a)))
    self.raw_d = nn.Parameter(torch.tensor(_inv_softplus(float(b - a), eps)))

b property

Return right shoulder, guaranteed > a via softplus offset.

forward

Compute linear S-shaped membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute linear S-shaped membership values for input tensor."""
    x = self._as_tensor(x)
    return ((x - self.a) / (self.b - self.a + self.eps)).clamp(0.0, 1.0)

LinZShapedMF

Bases: MembershipFunction

Linear Z-shaped membership from 1 to 0 between a and b.

Initialize with ramp start (a) and end (b); requires a < b.

Source code in highfis/memberships.py
def __init__(self, a: float, b: float, eps: float | None = None) -> None:
    """Initialize with ramp start (a) and end (b); requires a < b."""
    super().__init__(eps=eps)
    if not (a < b):
        raise ValueError("expected a < b")
    self.a = nn.Parameter(torch.tensor(float(a)))
    self.raw_d = nn.Parameter(torch.tensor(_inv_softplus(float(b - a), eps)))

b property

Return right foot, guaranteed > a via softplus offset.

forward

Compute linear Z-shaped membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute linear Z-shaped membership values for input tensor."""
    x = self._as_tensor(x)
    return 1.0 - ((x - self.a) / (self.b - self.a + self.eps)).clamp(0.0, 1.0)

MembershipFunction

Bases: nn.Module

Base class for differentiable membership functions in PyTorch.

Initialize base membership function.

Parameters:

Name Type Description Default
eps float | None

Numeric stability constant. None uses :func:torch.finfo machine epsilon for the current default dtype.

None
Source code in highfis/memberships.py
def __init__(self, eps: float | None = None) -> None:
    """Initialize base membership function.

    Args:
        eps: Numeric stability constant.  ``None`` uses
            :func:`torch.finfo` machine epsilon for the current
            default dtype.
    """
    super().__init__()
    self.eps = torch.finfo(torch.get_default_dtype()).eps if eps is None else float(eps)

PiMF

Bases: MembershipFunction

Pi-shaped membership with smooth S/Z transitions and flat top.

Initialize with four boundary points; requires a < b <= c < d.

Source code in highfis/memberships.py
def __init__(self, a: float, b: float, c: float, d: float, eps: float | None = None) -> None:
    """Initialize with four boundary points; requires a < b <= c < d."""
    super().__init__(eps=eps)
    if not (a < b <= c < d):
        raise ValueError("expected a < b <= c < d")
    eps_value = self.eps
    self.a = nn.Parameter(torch.tensor(float(a)))
    self.raw_d1 = nn.Parameter(torch.tensor(_inv_softplus(float(b - a), eps_value)))
    self.raw_d2 = nn.Parameter(torch.tensor(_inv_softplus(float(c - b + eps_value), eps_value)))
    self.raw_d3 = nn.Parameter(torch.tensor(_inv_softplus(float(d - c), eps_value)))

forward

Compute Pi-shaped membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute Pi-shaped membership values for input tensor."""
    x = self._as_tensor(x)
    a, b, c, d = self.points()

    t_s = ((x - a) / (b - a + self.eps)).clamp(0.0, 1.0)
    s = _smoothstep(t_s)

    t_z = ((x - c) / (d - c + self.eps)).clamp(0.0, 1.0)
    z = 1.0 - _smoothstep(t_z)

    zero = torch.zeros_like(x)
    one = torch.ones_like(x)
    return torch.where(x < a, zero, torch.where(x <= b, s, torch.where(x <= c, one, torch.where(x < d, z, zero))))

points

Return (a, b, c, d) with ordering guaranteed by softplus offsets.

Source code in highfis/memberships.py
def points(self) -> tuple[Tensor, Tensor, Tensor, Tensor]:
    """Return (a, b, c, d) with ordering guaranteed by softplus offsets."""
    b = self.a + F.softplus(self.raw_d1) + self.eps
    c = b + F.softplus(self.raw_d2)
    d = c + F.softplus(self.raw_d3) + self.eps
    return self.a, b, c, d

ProdSigmoidalMF

Bases: MembershipFunction

Product of two sigmoids: s1(x) * s2(x).

Initialize with slope and center parameters for each sigmoid.

Source code in highfis/memberships.py
def __init__(self, a1: float, center1: float, a2: float, center2: float, eps: float | None = None) -> None:
    """Initialize with slope and center parameters for each sigmoid."""
    super().__init__(eps=eps)
    self.a1 = nn.Parameter(torch.tensor(float(a1)))
    self.center1 = nn.Parameter(torch.tensor(float(center1)))
    self.a2 = nn.Parameter(torch.tensor(float(a2)))
    self.center2 = nn.Parameter(torch.tensor(float(center2)))

forward

Compute s1(x) * s2(x) membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute s1(x) * s2(x) membership values for input tensor."""
    x = self._as_tensor(x)
    s1 = torch.sigmoid(self.a1 * (x - self.center1))
    s2 = torch.sigmoid(self.a2 * (x - self.center2))
    return s1 * s2

SShapedMF

Bases: MembershipFunction

Smooth S-shaped membership from 0 to 1 between a and b.

Initialize with transition start (a) and end (b); requires a < b.

Source code in highfis/memberships.py
def __init__(self, a: float, b: float, eps: float | None = None) -> None:
    """Initialize with transition start (a) and end (b); requires a < b."""
    super().__init__(eps=eps)
    if not (a < b):
        raise ValueError("expected a < b")
    self.a = nn.Parameter(torch.tensor(float(a)))
    self.raw_d = nn.Parameter(torch.tensor(_inv_softplus(float(b - a), eps)))

b property

Return right shoulder, guaranteed > a via softplus offset.

forward

Compute smooth S-shaped membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute smooth S-shaped membership values for input tensor."""
    x = self._as_tensor(x)
    t = ((x - self.a) / (self.b - self.a + self.eps)).clamp(0.0, 1.0)
    return _smoothstep(t)

SigmoidalMF

Bases: MembershipFunction

Sigmoidal membership: 1 / (1 + exp(-a*(x-c))).

Initialize sigmoidal MF with slope a and center.

Source code in highfis/memberships.py
def __init__(self, a: float = 1.0, center: float = 0.0, eps: float | None = None) -> None:
    """Initialize sigmoidal MF with slope *a* and *center*."""
    super().__init__(eps=eps)
    self.a = nn.Parameter(torch.tensor(float(a)))
    self.center = nn.Parameter(torch.tensor(float(center)))

forward

Compute sigmoidal membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute sigmoidal membership values for input tensor."""
    x = self._as_tensor(x)
    return torch.sigmoid(self.a * (x - self.center))

TrapezoidalMF

Bases: MembershipFunction

Trapezoidal membership function defined by a, b, c, d.

Initialize trapezoidal MF with vertices a, b, c, d.

Source code in highfis/memberships.py
def __init__(
    self, a: float = -2.0, b: float = -1.0, c: float = 1.0, d: float = 2.0, eps: float | None = None
) -> None:
    """Initialize trapezoidal MF with vertices *a*, *b*, *c*, *d*."""
    super().__init__(eps=eps)
    if not (a <= b <= c <= d):
        raise ValueError("must satisfy a <= b <= c <= d")
    if a == d:
        raise ValueError("a and d must differ")
    self.a = nn.Parameter(torch.tensor(float(a)))
    self.b = nn.Parameter(torch.tensor(float(b)))
    self.c = nn.Parameter(torch.tensor(float(c)))
    self.d = nn.Parameter(torch.tensor(float(d)))

forward

Compute trapezoidal membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute trapezoidal membership values for input tensor."""
    x = self._as_tensor(x)
    left = (x - self.a) / (self.b - self.a + self.eps)
    right = (self.d - x) / (self.d - self.c + self.eps)
    return torch.clamp(torch.minimum(left, right), min=0.0, max=1.0)

TriangularMF

Bases: MembershipFunction

Triangular membership function defined by left, center, right.

Initialize triangular MF with left, center, right vertices.

Source code in highfis/memberships.py
def __init__(self, left: float = -1.0, center: float = 0.0, right: float = 1.0, eps: float | None = None) -> None:
    """Initialize triangular MF with *left*, *center*, *right* vertices."""
    super().__init__(eps=eps)
    if not (left <= center <= right):
        raise ValueError("must satisfy left <= center <= right")
    if left == right:
        raise ValueError("left and right must differ")
    self.left = nn.Parameter(torch.tensor(float(left)))
    self.center = nn.Parameter(torch.tensor(float(center)))
    self.right = nn.Parameter(torch.tensor(float(right)))

forward

Compute triangular membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute triangular membership values for input tensor."""
    x = self._as_tensor(x)
    left_slope = (x - self.left) / (self.center - self.left + self.eps)
    right_slope = (self.right - x) / (self.right - self.center + self.eps)
    return torch.clamp(torch.minimum(left_slope, right_slope), min=0.0, max=1.0)

ZShapedMF

Bases: MembershipFunction

Smooth Z-shaped membership from 1 to 0 between a and b.

Initialize with transition start (a) and end (b); requires a < b.

Source code in highfis/memberships.py
def __init__(self, a: float, b: float, eps: float | None = None) -> None:
    """Initialize with transition start (a) and end (b); requires a < b."""
    super().__init__(eps=eps)
    if not (a < b):
        raise ValueError("expected a < b")
    self.a = nn.Parameter(torch.tensor(float(a)))
    self.raw_d = nn.Parameter(torch.tensor(_inv_softplus(float(b - a), eps)))

b property

Return right foot, guaranteed > a via softplus offset.

forward

Compute smooth Z-shaped membership values for input tensor.

Source code in highfis/memberships.py
def forward(self, x: Tensor) -> Tensor:
    """Compute smooth Z-shaped membership values for input tensor."""
    x = self._as_tensor(x)
    t = ((x - self.a) / (self.b - self.a + self.eps)).clamp(0.0, 1.0)
    return 1.0 - _smoothstep(t)