Skip to content

bulanik.sets

sets

Membership functions, linguistic variables and t-norms / t-conorms.

The building blocks of fuzzy inference:

SIGMOID_EPS module-attribute

SIGMOID_EPS = 1e-09

Sigmoid memberships never reach exactly 0 or 1, so inverse of a sigmoid clamps alpha into \([\varepsilon, 1 - \varepsilon]\) with \(\varepsilon = 10^{-9}\) instead of returning \(\pm\infty\).

MembershipFunction dataclass

MembershipFunction(kind: str, params: tuple[float, ...])

A membership function \(\mu: \mathbb{R} \to [0, 1]\) of a named kind.

Build one with the factories (triangular, gaussian, ...). Calling the object evaluates it: mf(x) returns the membership degree, always inside \([0, 1]\), also for huge or infinite arguments. The supported kinds and their parameters are:

kind parameters \(\mu(x)\)
triangular a, b, c rises linearly from 0 at \(a\) to 1 at \(b\), falls to 0 at \(c\)
trapezoidal a, b, c, d rises from \(a\) to 1 at \(b\), 1 on \([b, c]\), falls to 0 at \(d\)
gaussian mean, sigma \(\exp(-(x - \text{mean})^2 / (2\sigma^2))\)
bell a, b, c \(1 / (1 + \lvert (x - c)/a \rvert^{2b})\)
sigmoid slope, center \(1 / (1 + e^{-\text{slope}\,(x - \text{center})})\)
linear_increasing a, b 0 for \(x \le a\), 1 for \(x \ge b\), linear between
linear_decreasing a, b 1 for \(x \le a\), 0 for \(x \ge b\), linear between

Three more kinds are the building blocks of interval type-2 sets (see bulanik.type2): gaussian_flat_top (m1, m2, sigma: 1 on \([m_1, m_2]\), else the Gaussian of the nearer mean), gaussian_min_pair (m1, m2, sigma: the minimum of the two Gaussians) and scaled_triangular (a, b, c, height: height times the triangle).

Direct construction validates the parameters as the factories do.

Parameters:

Name Type Description Default
kind str

One of the kinds above.

required
params tuple[float, ...]

The numeric parameters of the kind, in the order shown.

required

Raises:

Type Description
ValueError

If kind is unknown, the number of parameters is wrong, a parameter is not finite, or the parameters violate the rule of the kind (for example a <= b <= c for a triangle).

TypeError

If params is not a sequence of numbers.

Attributes:

Name Type Description
kind str

The kind name.

params tuple[float, ...]

The validated parameters, as a tuple of floats.

Examples:

>>> from bulanik.sets import triangular, gaussian
>>> mf = triangular(0, 5, 10)
>>> mf(2.5)
0.5
>>> round(gaussian(5, 1)(6), 4)
0.6065
>>> mf.kind, mf.params
('triangular', (0.0, 5.0, 10.0))

is_monotonic property

is_monotonic: bool

True for the kinds linear_increasing, linear_decreasing and sigmoid.

Only monotonic functions have an inverse.

Examples:

>>> from bulanik.sets import sigmoid, triangular
>>> sigmoid(1, 0).is_monotonic, triangular(0, 1, 2).is_monotonic
(True, False)

inverse

inverse(alpha: float) -> float

The \(x\) with \(\mu(x) = \alpha\) (monotonic kinds only).

For linear_increasing this is \(a (1 - \alpha) + b\alpha\) and for linear_decreasing \(b (1 - \alpha) + a\alpha\), exact at the ends. For sigmoid it is \(\text{center} - \ln(1/\alpha - 1)/\text{slope}\) with \(\alpha\) clamped into \([\varepsilon, 1 - \varepsilon]\), where \(\varepsilon\) is SIGMOID_EPS.

Parameters:

Name Type Description Default
alpha float

Membership level in \([0, 1]\).

required

Returns:

Type Description
float

The point x.

Raises:

Type Description
ValueError

If the function is not monotonic, or alpha is outside \([0, 1]\) or not finite.

Examples:

>>> from bulanik.sets import linear_increasing, sigmoid, triangular
>>> linear_increasing(0, 10).inverse(0.25)
2.5
>>> sigmoid(1, 0).inverse(0.5)
0.0
>>> triangular(0, 1, 2).inverse(0.5)
Traceback (most recent call last):
...
ValueError: inverse needs a monotonic membership function; triangular is not monotonic

LinguisticVariable

LinguisticVariable(name: str, universe: tuple[float, float], terms: Mapping[str, MembershipFunction])

A named variable on a universe (lo, hi) with named fuzzy terms.

Terms are looked up with variable["term"], which returns the MembershipFunction; the term order is the insertion order of terms.

Parameters:

Name Type Description Default
name str

The variable name, used in error messages and rule text.

required
universe tuple[float, float]

The pair (lo, hi) of the domain, with lo < hi.

required
terms Mapping[str, MembershipFunction]

Mapping from term name to membership function (at least one).

required

Raises:

Type Description
TypeError

If universe is not a pair, or a term is not a MembershipFunction.

ValueError

If lo >= hi, an end is not finite, or terms is empty.

Attributes:

Name Type Description
name

The variable name.

universe

The domain as a tuple of floats (lo, hi).

Examples:

>>> from bulanik.sets import LinguisticVariable, linear_decreasing, triangular, linear_increasing
>>> temp = LinguisticVariable("temp", (0, 40), {
...     "cold": linear_decreasing(10, 20),
...     "warm": triangular(10, 20, 30),
...     "hot": linear_increasing(20, 30),
... })
>>> temp
LinguisticVariable('temp', (0.0, 40.0), terms=('cold', 'warm', 'hot'))
>>> temp.fuzzify(15)
{'cold': 0.5, 'warm': 0.5, 'hot': 0.0}

term_names property

term_names: tuple[str, ...]

The term names, in insertion order.

Examples:

>>> from bulanik.sets import LinguisticVariable, triangular
>>> LinguisticVariable("x", (0, 1), {"lo": triangular(0, 0, 1)}).term_names
('lo',)

fuzzify

fuzzify(x: float) -> dict[str, float]

Membership of x in every term.

Parameters:

Name Type Description Default
x float

A crisp input inside the universe.

required

Returns:

Type Description
dict[str, float]

A dict from term name to membership degree, in term order.

Raises:

Type Description
ValueError

If x is not finite or lies outside the universe.

Examples:

>>> from bulanik.sets import LinguisticVariable, triangular
>>> v = LinguisticVariable("service", (0, 10), {"good": triangular(5, 10, 10)})
>>> v.fuzzify(7.5)
{'good': 0.5}
>>> v.fuzzify(11)
Traceback (most recent call last):
...
ValueError: 'service' input 11 is outside its universe [0, 10]

triangular

triangular(a: float, b: float, c: float) -> MembershipFunction

Triangle with feet a, c and peak b (a <= b <= c, a < c).

Parameters:

Name Type Description Default
a float

Left foot (membership 0).

required
b float

Peak (membership 1).

required
c float

Right foot (membership 0).

required

Returns:

Type Description
MembershipFunction

A MembershipFunction of kind "triangular".

Raises:

Type Description
ValueError

If the parameters are not finite or violate a <= b <= c and a < c.

Examples:

>>> from bulanik.sets import triangular
>>> tri = triangular(0, 5, 10)
>>> tri(5), tri(7.5), tri(11)
(1.0, 0.5, 0.0)
>>> triangular(3, 2, 1)
Traceback (most recent call last):
...
ValueError: triangular needs a <= b <= c and a < c, got (3.0, 2.0, 1.0)

trapezoidal

trapezoidal(a: float, b: float, c: float, d: float) -> MembershipFunction

Trapezoid with plateau [b, c] (a <= b <= c <= d, a < d).

Parameters:

Name Type Description Default
a float

Left foot (membership 0).

required
b float

Start of the plateau.

required
c float

End of the plateau.

required
d float

Right foot (membership 0).

required

Returns:

Type Description
MembershipFunction

A MembershipFunction of kind "trapezoidal".

Raises:

Type Description
ValueError

If the parameters are not finite or violate a <= b <= c <= d and a < d.

Examples:

>>> from bulanik.sets import trapezoidal
>>> trapezoidal(0, 1, 2, 3)(0.5), trapezoidal(0, 1, 2, 3)(1.5)
(0.5, 1.0)

gaussian

gaussian(mean: float, sigma: float) -> MembershipFunction

Gaussian \(\exp\!\left(-\frac{(x - \text{mean})^2}{2\sigma^2}\right)\) with \(\sigma > 0\).

Parameters:

Name Type Description Default
mean float

Centre of the bell (membership 1).

required
sigma float

Standard deviation, strictly positive.

required

Returns:

Type Description
MembershipFunction

A MembershipFunction of kind "gaussian".

Raises:

Type Description
ValueError

If a parameter is not finite or sigma <= 0.

Examples:

>>> from bulanik.sets import gaussian
>>> g = gaussian(5, 1)
>>> g(5), round(g(6), 4)
(1.0, 0.6065)

bell

bell(a: float, b: float, c: float) -> MembershipFunction

Generalized bell \(\dfrac{1}{1 + \lvert (x - c)/a \rvert^{2b}}\) with \(a > 0\), \(b > 0\).

Parameters:

Name Type Description Default
a float

Width, strictly positive.

required
b float

Slope of the shoulders, strictly positive.

required
c float

Centre (membership 1).

required

Returns:

Type Description
MembershipFunction

A MembershipFunction of kind "bell".

Raises:

Type Description
ValueError

If a parameter is not finite or a <= 0 or b <= 0.

Examples:

>>> from bulanik.sets import bell
>>> bell(2, 4, 6)(6), round(bell(2, 4, 6)(7), 4)
(1.0, 0.9961)

sigmoid

sigmoid(slope: float, center: float) -> MembershipFunction

Sigmoid \(\dfrac{1}{1 + e^{-\text{slope}\,(x - \text{center})}}\) with \(\text{slope} \ne 0\).

Increasing for a positive slope and decreasing for a negative one; membership 0.5 at center. It is monotonic, so it has an inverse.

Parameters:

Name Type Description Default
slope float

Steepness and direction, non-zero.

required
center float

The point where the membership is 0.5.

required

Returns:

Type Description
MembershipFunction

A MembershipFunction of kind "sigmoid".

Raises:

Type Description
ValueError

If a parameter is not finite or slope == 0.

Examples:

>>> from bulanik.sets import sigmoid
>>> sigmoid(1, 0)(0)
0.5

linear_increasing

linear_increasing(a: float, b: float) -> MembershipFunction

Ramp up: 0 for x <= a, 1 for x >= b, linear in between (a < b).

Parameters:

Name Type Description Default
a float

Last point with membership 0.

required
b float

First point with membership 1.

required

Returns:

Type Description
MembershipFunction

A MembershipFunction of kind "linear_increasing".

Raises:

Type Description
ValueError

If a parameter is not finite or a >= b.

Examples:

>>> from bulanik.sets import linear_increasing
>>> [linear_increasing(0, 10)(x) for x in (-1, 2.5, 12)]
[0.0, 0.25, 1.0]

linear_decreasing

linear_decreasing(a: float, b: float) -> MembershipFunction

Ramp down: 1 for x <= a, 0 for x >= b, linear in between (a < b).

Parameters:

Name Type Description Default
a float

Last point with membership 1.

required
b float

First point with membership 0.

required

Returns:

Type Description
MembershipFunction

A MembershipFunction of kind "linear_decreasing".

Raises:

Type Description
ValueError

If a parameter is not finite or a >= b.

Examples:

>>> from bulanik.sets import linear_decreasing
>>> [linear_decreasing(0, 10)(x) for x in (-1, 2.5, 12)]
[1.0, 0.75, 0.0]

t_norm

t_norm(a: float, b: float, kind: str = 'min') -> float

Fuzzy AND (t-norm): "min" \(\min(a, b)\) or "prod" \(a\,b\).

Parameters:

Name Type Description Default
a float

Membership degree in \([0, 1]\).

required
b float

Membership degree in \([0, 1]\).

required
kind str

"min" (default) or "prod".

'min'

Returns:

Type Description
float

The combined degree.

Raises:

Type Description
ValueError

If kind is unknown or a or b is outside \([0, 1]\).

Examples:

>>> from bulanik.sets import t_norm
>>> t_norm(0.3, 0.6), round(t_norm(0.3, 0.6, "prod"), 4)
(0.3, 0.18)

t_conorm

t_conorm(a: float, b: float, kind: str = 'max') -> float

Fuzzy OR (t-conorm): "max" \(\max(a, b)\) or "probor" \(a + b - ab\).

Parameters:

Name Type Description Default
a float

Membership degree in \([0, 1]\).

required
b float

Membership degree in \([0, 1]\).

required
kind str

"max" (default) or "probor".

'max'

Returns:

Type Description
float

The combined degree.

Raises:

Type Description
ValueError

If kind is unknown or a or b is outside \([0, 1]\).

Examples:

>>> from bulanik.sets import t_conorm
>>> t_conorm(0.3, 0.6), round(t_conorm(0.3, 0.6, "probor"), 4)
(0.6, 0.72)