Skip to content

Core Concepts

Understanding the fundamentals of fuzzy logic and inference.

Fuzzy Logic Basics

Degrees of Truth

Unlike classical logic where statements are either true (1) or false (0), fuzzy logic allows degrees of truth between 0.0 and 1.0:

# Classical logic
is_tall = True  # or False

# Fuzzy logic
is_tall = 0.75  # 75% tall

Real-World Example

Consider classifying someone's height:

from fuzzy_infer import fuzzy

kb = fuzzy()

# John is 5'10" (178cm)
kb.fact("height", ["john", 178])

# Fuzzy classification rules
kb.rule(
    when=("height", "?person", "?h"),
    then=("is-tall", "?person", degree="(?h - 150) / 50"),
    where=lambda b: b["?h"] > 175
)

kb.rule(
    when=("height", "?person", "?h"),
    then=("is-medium", "?person", degree="1.0 - abs(?h - 170) / 20"),
    where=lambda b: 160 <= b["?h"] <= 180
)

Facts

Facts are the building blocks of knowledge:

from fuzzy_infer import Fact

# Creating facts
fact1 = Fact("is-bird", ["robin"], degree=0.9)
fact2 = Fact("can-fly", ["robin"], degree=0.8)

# Facts have three components:
# 1. Predicate: what we're asserting ("is-bird")
# 2. Arguments: what it applies to (["robin"])
# 3. Degree: how true it is (0.9)

Rules

Rules encode if-then relationships:

from fuzzy_infer import Rule, Condition, Action

# Simple rule: birds can fly
rule = Rule(
    conditions=[Condition("is-bird", ["?x"])],
    actions=[Action("add", {"pred": "can-fly", "args": ["?x"], "degree": 0.85})],
    name="bird-flight"
)

# Using fluent API (much cleaner!)
kb.rule(
    when=("is-bird", "?x"),
    then=("can-fly", "?x", 0.85),
    name="bird-flight"
)

Pattern Variables

Variables (prefixed with ?) match any value:

# ?x matches any animal
kb.rule(
    when=("is-mammal", "?x"),
    then=("is-warm-blooded", "?x")
)

# Multiple variables
kb.rule(
    when=[("parent", "?x", "?y"), ("parent", "?y", "?z")],
    then=("grandparent", "?x", "?z")
)

# Constraints on variables
kb.rule(
    when=("age", "?person", "?age"),
    then=("is-adult", "?person"),
    where=lambda b: b["?age"] >= 18
)

Forward Chaining

FuzzyInfer uses forward chaining (data-driven inference):

graph LR
    A[Initial Facts] --> B[Apply Rules]
    B --> C[Generate New Facts]
    C --> D{New Facts?}
    D -->|Yes| B
    D -->|No| E[Complete]

Inference Process

  1. Start with initial facts
  2. Find rules whose conditions match
  3. Execute rule actions (add new facts)
  4. Repeat until no new facts generated
# Initial facts
kb.fact("is-bird", ["robin"])
kb.fact("is-bird", ["eagle"])

# Rule
kb.rule(
    when=("is-bird", "?x"),
    then=("can-fly", "?x")
)

# Before inference
print(kb.query("can-fly"))  # []

# Run inference
kb.infer()

# After inference
print(kb.query("can-fly"))  # [robin, eagle]

Fuzzy Operations

Fuzzy AND (T-norms)

When multiple conditions must be met:

# Minimum (default)
kb.rule(
    when=[
        ("condition-a", "?x", degree=0.8),
        ("condition-b", "?x", degree=0.6)
    ],
    then=("result", "?x"),  # degree = min(0.8, 0.6) = 0.6
    fuzzy_and="min"
)

# Product
kb.rule(
    when=[("a", "?x"), ("b", "?x")],
    then=("result", "?x"),  # degree = 0.8 * 0.6 = 0.48
    fuzzy_and="product"
)

Fuzzy OR (T-conorms)

When any condition is sufficient:

# Maximum (default)
kb.rule(
    when_any=[
        ("option-a", "?x", degree=0.7),
        ("option-b", "?x", degree=0.9)
    ],
    then=("selected", "?x"),  # degree = max(0.7, 0.9) = 0.9
    fuzzy_or="max"
)

Fuzzy NOT

Negation in fuzzy logic:

# Standard negation
kb.rule(
    when=("is-cold", "?x", degree="?d"),
    then=("is-warm", "?x", degree="1.0 - ?d")
)

Degree Arithmetic

Compute degrees dynamically:

# Degree variables
kb.rule(
    when=("probability", "?event", degree="?p"),
    then=("unlikely", "?event", degree="1.0 - ?p")
)

# Complex calculations
kb.rule(
    when=[
        ("feature-a", "?x", degree="?a"),
        ("feature-b", "?x", degree="?b")
    ],
    then=("combined", "?x", degree="(?a + ?b) / 2")  # Average
)

# Hedges (linguistic modifiers)
kb.rule(
    when=("tall", "?x", degree="?d"),
    then=("very-tall", "?x", degree="?d * ?d")  # Square for "very"
)

Conflict Resolution

When multiple rules could fire:

Priority-Based

# Higher priority rules fire first
kb.rule(
    when=("emergency", "?x"),
    then=("handle", "?x"),
    priority=10  # High priority
)

kb.rule(
    when=("request", "?x"),
    then=("handle", "?x"),
    priority=1  # Low priority
)

Specificity-Based

More specific rules override general ones:

# General rule
kb.rule(
    when=("is-bird", "?x"),
    then=("can-fly", "?x", 0.9)
)

# Specific exception
kb.rule(
    when=[("is-bird", "?x"), ("is-penguin", "?x")],
    then=("can-fly", "?x", 0.0),  # Penguins can't fly
    priority=5  # Higher priority
)

Knowledge Base

The knowledge base stores facts and rules:

from fuzzy_infer import FuzzyInfer

# Create knowledge base
kb = FuzzyInfer()

# Add facts
kb.add_fact(Fact("is-mammal", ["dog"]))
kb.add_facts([
    Fact("is-mammal", ["cat"]),
    Fact("is-bird", ["robin"])
])

# Add rules
kb.add_rule(rule)

# Query facts
mammals = kb.query("is-mammal")  # Returns all mammals
specific = kb.query("is-mammal", ["dog"])  # Check specific

# Run inference
result = kb.run()  # Returns InferenceResult
print(f"Generated {result.facts_added} new facts")

JSONL Serialization

FuzzyInfer uses JSON Lines for interoperability:

{"type": "fact", "pred": "is-bird", "args": ["robin"], "degree": 0.9}
{"type": "rule", "name": "fly", "when": [{"pred": "is-bird", "args": ["?x"]}], "then": [{"action": "add", "pred": "can-fly", "args": ["?x"]}]}

This enables Unix-style pipelines:

# Chain with other tools
cat facts.jsonl |
fuzzy-infer run rules.jsonl |
jq 'select(.pred == "can-fly")' |
sort -t'"' -k8 -rn  # Sort by degree

Performance Considerations

Fact Storage

  • Facts indexed by (predicate, arguments)
  • O(1) lookup for specific facts
  • O(n) for pattern matching

Rule Matching

  • Forward chaining is exhaustive
  • Each iteration checks all rules
  • Complexity: O(rules × facts)

Optimization Tips

  1. Use specific patterns: More constraints = faster matching
  2. Order rules by frequency: Common rules first
  3. Limit inference depth: Set max iterations
  4. Stream processing: Process in chunks for large datasets

Next Steps