FUZZY LOGIC IN R — WITH PSEUDO CODE MATH + PHYSICAL ANCHORS
Fuzzy Logic is a tremendous tool for capturing the uncertainty and ‘qualitative nature’ of biological systems. We often shy away from biologic solutions because it is ‘untestable’ or ‘unknowable’ but that’s not strictly true. It does take some creativity to think in terms of ranges and constraints as opposed to a single ‘right’ answer, but this kind of not-linear thinking (in this case, it’s trapazoidal thinking) could be the key to better modeling chaos and dynamical systems. I’ve been experimenting with developing fuzzy models and dynamic models in R (a free-to-use code language) and I’m getting some really interesting results. I’m not sure quite how to use it yet in the field (or with actual clients), but I see a tremendous potential to be able to communicate biological productivity and likelihood of success using this type of work. Here, I’m simulating a biological system under stress to see where it ‘fails’. This is directly tied to my wastewater work, as microbial guilds fail under different oxygen amounts, which changes what nutrients are consumed v. discharged back into the environment. From a pollution prevention and ecosystem management perspective, this is a big deal.
Below, every mathematical expression is rewritten using R‑style pseudo‑code variables so you can see how the math becomes code.
1. Install and Load Packages
install.packages("sets")
library(sets)
Teaching note:
This loads the fuzzy‑logic engine.
Think of it as loading the “mathematical vocabulary” for fuzzy sets.
2. Define a Fuzzy Variable (Dissolved Oxygen)
do <- fuzzy_variable(
low = fuzzy_trapezoid(0, 0, 0.5, 1.5),
medium = fuzzy_triangle(0.5, 2, 3.5),
high = fuzzy_trapezoid(3, 4, 10, 10)
)
Pseudo‑code math
mu_low_DO(x) = trapezoid(x; a=0, b=0, c=0.5, d=1.5)
mu_med_DO(x) = triangle(x; a=0.5, b=2, c=3.5)
mu_high_DO(x) = trapezoid(x; a=3, b=4, c=10, d=10)
Physical anchor points
x = measured DO in mg/L
mu_low_DO(x) = “how much the microbes experience DO as LOW”
mu_med_DO(x) = “how much the microbes experience DO as MODERATE”
mu_high_DO(x) = “how much the microbes experience DO as HIGH”
Microbes don’t flip states — they slide across these membership curves.
3. Evaluate Membership for a Real DO Value
value <- 0.8
do(value)
Pseudo‑code math
low_membership = mu_low_DO(0.8)
medium_membership = mu_med_DO(0.8)
high_membership = mu_high_DO(0.8)
Physical anchor
low_membership = degree of oxygen stress
medium_membership = partial comfort zone
high_membership = how much the system is oxygen‑rich
This is how microbes “feel” DO.
4. Add Temperature and Define Nitrifier Health
temp <- fuzzy_variable(
cold = fuzzy_trapezoid(0, 0, 5, 10),
moderate = fuzzy_triangle(8, 15, 22),
warm = fuzzy_trapezoid(20, 25, 35, 35)
)
health <- fuzzy_variable(
poor = fuzzy_trapezoid(0, 0, 0.2, 0.4),
fair = fuzzy_triangle(0.3, 0.5, 0.7),
good = fuzzy_trapezoid(0.6, 0.8, 1, 1)
)
Pseudo‑code math
mu_cold_T(x) = trapezoid(x; 0,0,5,10)
mu_mod_T(x) = triangle(x; 8,15,22)
mu_warm_T(x) = trapezoid(x; 20,25,35,35)
mu_poor_H(y) = trapezoid(y; 0,0,0.2,0.4)
mu_fair_H(y) = triangle(y; 0.3,0.5,0.7)
mu_good_H(y) = trapezoid(y; 0.6,0.8,1,1)
Physical anchors
x = temperature in °C
y = nitrifier health index (0–1)
mu_cold_T(x) = how much the microbes experience the water as COLD
mu_poor_H(y) = how much the system is in POOR health
These are ecological “feeling curves.”
5. Build Fuzzy Rules
rules <- set(
fuzzy_rule(do %is% low && temp %is% cold, health %is% poor),
fuzzy_rule(do %is% medium && temp %is% moderate, health %is% fair),
fuzzy_rule(do %is% high && temp %is% warm, health %is% good)
)
Pseudo‑code math
rule1_strength = min( mu_low_DO(DO), mu_cold_T(T) )
rule2_strength = min( mu_med_DO(DO), mu_mod_T(T) )
rule3_strength = min( mu_high_DO(DO), mu_warm_T(T) )
Physical anchors
rule1_strength = “how strongly the system is in LOW DO + COLD conditions”
rule2_strength = “how strongly the system is in moderate conditions”
rule3_strength = “how strongly the system is in ideal conditions”
The min() operator is the fuzzy AND (t‑norm).
6. Run Fuzzy Inference
fis <- fuzzy_system(
variables = set(do = do, temp = temp, health = health),
rules = rules
)
result <- fuzzy_inference(fis, list(do = 0.8, temp = 9))
result
Pseudo‑code math
# Step 1: compute memberships
mu_low = mu_low_DO(0.8)
mu_med = mu_med_DO(0.8)
mu_high = mu_high_DO(0.8)
mu_cold = mu_cold_T(9)
mu_mod = mu_mod_T(9)
mu_warm = mu_warm_T(9)
# Step 2: rule firing strengths
alpha1 = min(mu_low, mu_cold)
alpha2 = min(mu_med, mu_mod)
alpha3 = min(mu_high, mu_warm)
# Step 3: clip output fuzzy sets
poor_clipped(y) = min( mu_poor_H(y), alpha1 )
fair_clipped(y) = min( mu_fair_H(y), alpha2 )
good_clipped(y) = min( mu_good_H(y), alpha3 )
# Step 4: aggregate
mu_output(y) = max( poor_clipped(y), fair_clipped(y), good_clipped(y) )
# Step 5: defuzzify (centroid)
health_score = sum( y * mu_output(y) ) / sum( mu_output(y) )
Physical anchors
alpha1 = strength of “system is stressed”
alpha2 = strength of “system is okay”
alpha3 = strength of “system is thriving”
health_score = final continuous nitrifier health index
This is a biologically realistic health score.
7. Visualize the Fuzzy Sets
plot(do)
plot(temp)
plot(health)
Physical anchor
These plots show the “perception curves” of the microbes.
8. Advanced: Learning Fuzzy Systems with frbs
install.packages("frbs")
library(frbs)
data <- your_training_data
range.data <- apply(data, 2, range)
model <- frbs.learn(
data.train = data,
range.data = range.data,
method.type = "ANFIS",
control = list(num.labels = 3, type.mf = "GAUSSIAN")
)
Pseudo‑code math
# ANFIS learns:
# - membership function centers
# - membership function widths
# - rule weights
# using gradient descent
Physical anchor
This is like letting the microbial community teach you how it responds to stress.
Here is the original model:
1. Install and load the packages
r
install.packages("sets")
library(sets)
Teaching point: sets gives you the mathematical objects of fuzzy logic:
fuzzy sets
membership functions
linguistic variables
fuzzy rules
inference operators
This is the “raw math” package.
2. Define a fuzzy variable (e.g., DO concentration)
Code
r
do <- fuzzy_variable(
low = fuzzy_trapezoid(0, 0, 0.5, 1.5),
medium = fuzzy_triangle(0.5, 2, 3.5),
high = fuzzy_trapezoid(3, 4, 10, 10)
)
Teaching points
Mathematically:
A fuzzy set is a function
mapping each DO value to a degree of membership.
fuzzy_trapezoid(a, b, c, d) defines a membership function that rises from 0→1 between a and b, stays at 1 between b and c, and falls from 1→0 between c and d.
fuzzy_triangle(a, b, c) is a peak at b with linear slopes.
Why this matters biologically: Microbes don’t flip from “low DO” to “medium DO.” They slide across a gradient. This code literally encodes that sliding.
3. Evaluate membership for a real DO value
Code
r
value <- 0.8
do(value)
Teaching points
Mathematically: This computes
and returns something like:
Code
low medium high
0.6 0.3 0
Why this matters biologically: This is exactly how nitrifiers “feel” oxygen:
60% in the “low oxygen stress” regime
30% in the “moderate oxygen” regime
0% in the “high oxygen” regime
This is a biologically realistic state description.
4. Build a fuzzy rule base
Let’s add temperature and define a rule for nitrifier health.
Code
r
temp <- fuzzy_variable(
cold = fuzzy_trapezoid(0, 0, 5, 10),
moderate = fuzzy_triangle(8, 15, 22),
warm = fuzzy_trapezoid(20, 25, 35, 35)
)
health <- fuzzy_variable(
poor = fuzzy_trapezoid(0, 0, 0.2, 0.4),
fair = fuzzy_triangle(0.3, 0.5, 0.7),
good = fuzzy_trapezoid(0.6, 0.8, 1, 1)
)
rules <- set(
fuzzy_rule(do %is% low && temp %is% cold, health %is% poor),
fuzzy_rule(do %is% medium && temp %is% moderate, health %is% fair),
fuzzy_rule(do %is% high && temp %is% warm, health %is% good)
)
Teaching points
Mathematically:
do %is% low computes the membership degree of DO in the fuzzy set “low.”
&& is a fuzzy AND, typically implemented as a t-norm (min operator by default).
A rule evaluates to a degree of truth:
The output fuzzy set (e.g., “poor health”) is clipped at height α.
Why this matters biologically: This is how you encode expert intuition: “If DO is low AND temperature is cold, nitrifiers struggle.”
The rule doesn’t fire ON or OFF. It fires partially, just like biological stress.
5. Run fuzzy inference
Code
r
fis <- fuzzy_system(
variables = set(do = do, temp = temp, health = health),
rules = rules
)
result <- fuzzy_inference(fis, list(do = 0.8, temp = 9))
result
Teaching points
Mathematically:
Compute membership degrees for each input.
Evaluate each rule’s firing strength α.
Clip each output fuzzy set at α.
Aggregate all clipped outputs using a fuzzy OR (max operator).
Defuzzify (default: centroid):
Why this matters biologically: The centroid defuzzification gives you a continuous health score, not a category. This is perfect for:
early warning systems
process stability indices
risk scoring
adaptive control
6. Visualize the fuzzy sets (critical for intuition)
Code
r
plot(do)
plot(temp)
plot(health)
Teaching points
Visualization is not cosmetic — it’s conceptual. You see:
where categories overlap
how steep transitions are
how sensitive the system is to small changes
This is how you debug your mental model of the biology.
7. A more advanced example: using frbs for ANFIS or hybrid systems
If you want machine‑learning‑enhanced fuzzy modeling:
r
install.packages("frbs")
library(frbs)
data <- your_training_data
range.data <- apply(data, 2, range)
model <- frbs.learn(
data.train = data,
range.data = range.data,
method.type = "ANFIS",
control = list(num.labels = 3, type.mf = "GAUSSIAN")
)
Teaching points
frbs can learn membership functions from data.
ANFIS uses gradient descent to tune fuzzy sets.
This is ideal for biological systems with hidden nonlinearities.