This project is part of the @thi.ng/umbrella monorepo and anti-framework.
Fuzzy logic operators & configurable rule inferencing engine.
- Entirely declarative & functional approach
- Fully type checked
- Fuzzy set domain shaping & composition functions (incl. negated / inverse)
- Various T-norms & S-norms, incl. parametric versions
- Rules with multiple inputs/outputs and arbitrary term combinators (i.e.
T-norms). Syntax sugar for common
and
/or
rules. - Defuzzification via customizable strategies and options to balance precision
vs. performance
- Maxima: First, Last, Mean
- Center-of-Gravity (COG)
- Linguistic variable creation and term/set classification for given domain values
- Fuzzy set visualization (via @thi.ng/fuzzy-viz)
- Fuzzy Logic (Wikipedia)
- T-norm (Wikipedia)
- Fuzzy Logic - University of Western Australia
- Introduction to Fuzzy Logic
- Defuzzification (Wikipedia)
- Defuzzification methods
- Comparison of the COG Defuzzification Technique
STABLE - used in production
Search or submit any issues for this package
- @thi.ng/fuzzy-viz - Visualization, instrumentation & introspection utils for @thi.ng/fuzzy
yarn add @thi.ng/fuzzy
ES module import:
<script type="module" src="https://cdn.skypack.dev/@thi.ng/fuzzy"></script>
For Node.js REPL:
const fuzzy = await import("@thi.ng/fuzzy");
Package sizes (brotli'd, pre-treeshake): ESM: 1.62 KB
(See tests for more usage examples).
Generators:
constant()
point()
ramp()
/invRamp()
triangle()
trapezoid()
sigmoid()
/invSigmoid()
gaussian()
Combinators:
negate()
weighted()
alphaCut()
/invAlphaCut()
compose()
/intersect()
/union()
Linguistic variables (short: L-vars) are groupings of named (and possibly overlapping) fuzzy sets within a given value domain. The can be used standalone or as inputs/outputs in rules (further below).
The @thi.ng/fuzzy-viz package provides utilities to visualize the fuzzy sets of an L-var.
// temperature sets (in celsius)
const temp = variable(
// value domain
[-20, 40],
{
freezing: invSigmoid(0, 2),
cold: trapezoid(-1, 2, 16, 20),
warm: trapezoid(15, 20, 30, 34),
hot: sigmoid(32, 2)
}
);
// evaluate all fuzzy sets for given domain value
evaluate(temp, 18)
// {
// freezing: 2.220446049250313e-16,
// cold: 0.5,
// warm: 0.6,
// hot: 6.914400106935423e-13
// }
evaluate(temp, 28)
// {
// freezing: 0,
// cold: 0,
// warm: 1,
// hot: 0.0003353501304664781
// }
// classify temperature (min confidence 33%, default: 50%)
classify(temp, 28, 0.33)
// "warm"
Example taken from Franck Dernoncourt's Introduction to Fuzzy Logic:
// define fuzzy input variables
const inputs = {
food: variable([0, 10], {
awful: invRamp(1, 3),
delicious: ramp(7, 9),
}),
service: variable([0, 10], {
poor: gaussian(0, 1.5),
good: gaussian(5, 1.5),
excellent: gaussian(10, 1.5),
}),
};
const outputs = {
tip: variable([0, 30], {
low: triangle(0, 5, 10),
medium: triangle(10, 15, 20),
high: triangle(20, 25, 30),
}),
};
// l-vars, rules and defuzzification are using generics for type safety
// we define these 2 type aliases for brevity
type I = typeof inputs;
type O = typeof outputs;
// rule definitions:
// if service is poor OR food is awful -> tip is low
// if service is normal -> tip is medium
// if service is excellent OR food is delicious -> tip is high
const rules = [
or<I, O>({ food: "awful", service: "poor" }, { tip: "low" }),
or<I, O>({ service: "good" }, { tip: "medium" }),
or<I, O>({ food: "delicious", service: "excellent" }, { tip: "high" }),
];
// defuzzification using default center-of-gravity strategy
defuzz(
inputs,
outputs,
rules,
// input values
{ food: 7.32, service: 7.83 },
);
// { tip: 22.650000000000034 }
// defuzz with custom strategy (note: each has further config options)
defuzz(
inputs,
outputs,
rules,
// input values
{ food: 7.32, service: 7.83 },
// custom defuzz strategy
meanOfMaximaStrategy()
);
// { tip: 25.050000000000043 }
Note: The results are slightly different than those in the textbook example, due
to different gaussian
fuzzy sets used for the service
L-var.
Using instrumentStrategy()
from the upcoming
@thi.ng/fuzzy-viz
package, we can also visualize the final, transformed fuzzy sets used to compute
crisp results and highlight the position of the crisp result value.
Here is the ASCII art output for the
centroidStrategy
and using tnormMin
(the default) to transform each rule's output set(s):
// wrap existing strategy
const strat = instrumentStrategy(centroidStrategy(), fuzzySetToAscii());
// call defuzz as normal
defuzz(inputs, outputs, rules, strat);
// obtain secondary results via deref()
console.log(strat.deref()[0]);
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅.....
.......................................................................▁|██████████████████████▇....
......................................................................▁█|███████████████████████▇...
....................................▅▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▅....▂██|████████████████████████▇..
...................................▅█████████████████████████████▅..▂███|██████████████████████████.
..................................▅███████████████████████████████▅▂████|███████████████████████████
^ 21.52
Different results can be obtained by adjusting the
T-norm used to transform each rule's
output sets, here using tnormHamacher(2)
.
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|..........................
.........................................................................|.........▃▂...............
.........................................................................|.......▃███▆▂.............
.........................................................................|....▁▅███████▇▃...........
................................................▁▃▆▃▁....................|.▁▄▇████████████▆▃........
...........................................▁▂▄▆███████▆▄▂▁..............▂|▇██████████████████▆▃▁....
...................................▁▂▃▄▅▆▇█████████████████▇▆▅▄▃▂▁..▂▄▆██|███████████████████████▅▃▁
^ 21.84
...or using tnormAczelAlsina(2)
(there're many more available):
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|...........................
........................................................................|.....▂▃▄▅▅▅▅▅▄▄▃▁..........
........................................................................|.▂▅▇██████████████▆▄.......
........................................................................|▇████████████████████▆▂....
........................................▁▂▃▄▄▅▅▆▆▆▆▆▆▆▅▅▄▄▃▂▁.........▃█|███████████████████████▆▁..
....................................▂▄▇███████████████████████▇▄▂....▆██|█████████████████████████▄.
..................................▃▇█████████████████████████████▇▃▂████|██████████████████████████▇
^ 21.58
Just for illustration purposes (and using a different example), SVG output can
be obtained by merely switching to another instrumentation function (here
fuzzySetToSvg()
):
If this project contributes to an academic publication, please cite it as:
@misc{thing-fuzzy,
title = "@thi.ng/fuzzy",
author = "Karsten Schmidt",
note = "https://thi.ng/fuzzy",
year = 2020
}
© 2020 - 2023 Karsten Schmidt // Apache License 2.0