Fuzzy logic, introduced by Lotfi Zadeh in 1965, replaces the crisp true/false of classical logic with degrees of truth in [0, 1]. It is designed for vague, graded concepts that people use naturally — "warm", "fast", "high risk" — and lets you build controllers and decision rules directly from linguistic statements such as IF temperature is hot AND humidity is high THEN fan speed is fast. It is widely used in embedded control (appliances, HVAC, automotive subsystems, cameras), in scoring systems, and in fuzzy matching of records.
Part of Knowledge & Reasoning. It extends Rule-based Systems with graded conditions and contrasts with Bayesian Networks, which model uncertainty rather than vagueness.
A classical set has a characteristic function that returns 0 or 1. A fuzzy set A on a universe X has a membership function μA: X → [0, 1]. Common shapes are triangular, trapezoidal, Gaussian and sigmoid. A linguistic variable such as temperature is partitioned into overlapping terms (cold, warm, hot); overlap is what makes the controller's output change smoothly as input changes. Converting a crisp input into memberships is called fuzzification. The figure uses the same membership functions as the code in section 5.
Logical connectives generalize to functions on [0, 1]. AND uses a t-norm, OR a t-conorm, NOT a complement:
| Connective | Zadeh (min/max) | Product family | Łukasiewicz |
|---|---|---|---|
| A AND B | min(a, b) | a · b | max(0, a + b − 1) |
| A OR B | max(a, b) | a + b − a·b | min(1, a + b) |
| NOT A | 1 − a | 1 − a | 1 − a |
All reduce to Boolean logic on 0/1 inputs. Note that the law of the excluded middle fails: with min/max, A OR NOT A for a = 0.5 is only 0.5. Hedges modify terms: "very A" is commonly μ2, "somewhat A" is √μ.
A fuzzy inference system (FIS) runs every rule in parallel. Each rule's firing strength is its antecedent evaluated with the chosen t-norm. In a Mamdani system the consequent is a fuzzy set, clipped (min) or scaled (product) by the firing strength; all clipped sets are aggregated with max into one output fuzzy set. In a Takagi-Sugeno-Kang (Sugeno) system each consequent is a crisp function such as z = p·x + q·y + r, and the output is the strength-weighted average of the rule outputs, with no separate defuzzification step. Sugeno systems are cheaper, easier to tune from data, and are the basis of ANFIS (adaptive neuro-fuzzy inference), which learns membership parameters by gradient descent.
Defuzzification turns the aggregated output set into one actionable number. The centroid (center of gravity) is the most common and gives smooth behavior. Alternatives are the bisector, mean of maxima, and smallest or largest of maxima; maxima-based methods are cheaper but can jump abruptly when rule strengths change.
import numpy as np
def tri(x, a, b, c):
"""Triangular membership; a == b or b == c gives a shoulder."""
x = np.asarray(x, dtype=float)
left = np.ones_like(x) if b == a else (x - a) / (b - a)
right = np.ones_like(x) if c == b else (c - x) / (c - b)
return np.clip(np.minimum(left, right), 0, 1)
# Input terms
temp = {"cold": (0, 0, 17), "warm": (10, 20, 30), "hot": (22, 32, 32)}
hum = {"dry": (0, 0, 60), "humid": (40, 100, 100)}
# Output universe and terms (fan speed %)
z = np.linspace(0, 100, 501)
fan = {"slow": tri(z, 0, 0, 50), "medium": tri(z, 20, 50, 80), "fast": tri(z, 50, 100, 100)}
rules = [ # (temp term, humidity term or None, output term)
("cold", None, "slow"),
("warm", "dry", "medium"),
("warm", "humid", "fast"),
("hot", None, "fast"),
]
def control(t, h):
mt = {k: float(tri(t, *v)) for k, v in temp.items()}
mh = {k: float(tri(h, *v)) for k, v in hum.items()}
agg = np.zeros_like(z)
for tt, ht, out in rules:
strength = mt[tt] if ht is None else min(mt[tt], mh[ht]) # AND = min
agg = np.maximum(agg, np.minimum(strength, fan[out])) # clip, then max
if agg.sum() == 0:
return 0.0
return float((agg * z).sum() / agg.sum()) # centroid
for t, h in [(12, 30), (21, 30), (21, 85), (35, 50)]:
print(f"temp={t:>2} C humidity={h:>2}% -> fan={control(t, h):5.1f}%")
For larger systems, scikit-fuzzy provides membership functions, rule objects and a control-system simulator; simpful supports both Mamdani and Sugeno.
Both use numbers in [0, 1], but they answer different questions. Probability measures uncertainty about a crisp fact: "there is a 0.7 chance it will be above 25 °C tomorrow". Membership measures the degree to which a known fact fits a vague concept: "25 °C is 0.7 hot". Observing the outcome resolves probability; it does not resolve vagueness. The two can be combined (fuzzy random variables, possibility theory), but mixing them casually leads to incoherent models.
Take the fan controller above with temp = 21 °C and humidity = 30%. Fuzzification: the triangle warm = (10, 20, 30) gives 21 °C a membership of 0.9, while cold and hot are both 0. For humidity, dry = (0, 0, 60) gives 0.5 and humid gives 0. Rule evaluation: only "warm AND dry → medium" fires, with strength min(0.9, 0.5) = 0.5. Implication: the medium output triangle is clipped at height 0.5, producing a trapezoid. Aggregation: with one active rule, the aggregate is that trapezoid. Defuzzification: the trapezoid is symmetric around 50, so its centroid, the fan speed, is 50%.
Raise humidity to 85% and the picture changes. Now humid is 0.75 and dry is 0, so "warm AND humid → fast" fires with strength min(0.9, 0.75) = 0.75. The fast shoulder clipped at 0.75 has a centroid near 82.6%, so the fan speeds up smoothly rather than jumping between fixed settings. Between those two humidity values, both rules fire partially and the output slides continuously, which is exactly the property that makes fuzzy controllers pleasant to tune.
Writing the rules as a grid, one axis per input and one cell per combination of terms, makes gaps and contradictions visible at a glance. Each cell holds the consequent term. Neighbouring cells should usually differ by at most one output term; a jump from "slow" to "fast" between adjacent cells produces a steep, twitchy control surface. Practical guidance: