Measures & relations¶
Two modules cover the parts of classical fuzzy set theory that the inference engines hide behind an efficient shortcut.
Measures on fuzzy sets¶
fuzzytool.measures works on sampled membership degrees
— a 1-D array over a shared universe, which is what
variable.terms[name](variable.universe) gives you. sample() accepts a
membership function, an array, or an interval type-2 set (which collapses to the
midpoint of its footprint).
import numpy as np, fuzzytool as fz
from fuzzytool import measures as ms
x = np.linspace(0, 10, 501)
a = ms.sample(fz.tri(2, 4, 6), x)
b = ms.sample(fz.tri(3, 5, 7), x)
Descriptors — height, is_normal, support, core, alpha_cut,
cardinality, relative_cardinality:
Distances — hamming, euclidean, minkowski, each with a normalized
option so the value does not depend on how finely you sampled the universe.
Similarity and inclusion — jaccard, dice, subsethood (Kosko's degree
to which A ⊆ B) and consistency (max min(a, b): can these two sets hold at
once?). consistency == 0 between two antecedent terms means the rules using
them can never fire together.
Fuzziness — fuzzy_entropy (De Luca-Termini, normalized to [0, 1]) and
index_of_fuzziness. Both are 0 for a crisp set and 1 when every degree is 0.5.
These are what audit uses to decide when two terms say the same
thing.
Fuzzy relations¶
A fuzzy relation on X × Y is a fuzzy set of pairs: a matrix R[i, j]
grading how strongly x_i relates to y_j. This is what a fuzzy rule is,
before any engine optimizes it away.
from fuzzytool import relations as rel
a = fz.tri(2, 4, 6)(x)
b = fz.tri(5, 7, 9)(x)
r = rel.cartesian(a, b) # the relation "IF x is A THEN y is B"
out = rel.cri(a, r) # compositional rule of inference
np.allclose(out, b) # True — fuzzy modus ponens
cri is the compositional rule of inference:
B' = A' ∘ R. Feed in the antecedent you built the relation from and you get
the consequent back; feed in a different observation and you get the partial
match a real inference produces.
Other operations:
| Function | What it does |
|---|---|
compose(r, s) |
sup-t composition R ∘ S (max-min by default) |
projection(r, axis) |
the shadow a relation casts on one universe |
cylindrical_extension(a, n) |
lift a set to a relation that ignores the other axis |
inverse, union, intersection |
the obvious pointwise operations |
implication_relation(a, b, kind) |
Mamdani, Larsen, Łukasiewicz, Gödel, Kleene-Dienes, Zadeh |
transitive_closure(r) |
max-min closure; turns a proximity into an equivalence |
is_reflexive / is_symmetric / is_transitive |
the relation's properties |
Every operation takes its connectives by name, so composing with the product t-norm is one argument away:
Why the implication kind matters¶
implication_relation shows something the Mamdani engine papers over. Mamdani
and Larsen are conjunctive readings (min and product); the rest are
genuine implications. They agree where the antecedent fires strongly and differ
sharply where it does not — a Łukasiewicz implication approaches 1 when the
antecedent is near 0, whereas Mamdani goes to 0. If you have ever wondered why
"IF x is A THEN y is B" behaves oddly outside A's support, this is where to look.
Relational clustering in three lines¶
The transitive closure of a fuzzy proximity relation is a fuzzy equivalence relation, and its alpha-cuts are a hierarchy of crisp partitions:
similarity = np.array([[1.0, 0.8, 0.2],
[0.8, 1.0, 0.6],
[0.2, 0.6, 1.0]])
equivalence = rel.transitive_closure(similarity)
groups = equivalence >= 0.6 # the partition at level 0.6
See also: Auditing a rule base, Fuzzy clustering.