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:

  1. Compute membership degrees for each input.

  2. Evaluate each rule’s firing strength α.

  3. Clip each output fuzzy set at α.

  4. Aggregate all clipped outputs using a fuzzy OR (max operator).

  5. 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.

OSUZ504 Tech