Skip to content

Core Concepts

FuzzyInfer extends classical rule-based inference with fuzzy logic, enabling reasoning with uncertainty and degrees of belief.

Facts

Facts represent knowledge with associated degrees of belief (certainty). Unlike classical logic where facts are either true or false, fuzzy facts have a degree of membership between 0.0 and 1.0.

Structure

A fact consists of three components:

  • Predicate: The property or relationship (e.g., is-mammal, temperature)
  • Arguments: One or more values (e.g., ["dog"], ["room-1", "hot"])
  • Degree: Confidence level from 0.0 (completely uncertain) to 1.0 (completely certain)

Examples

from fuzzy_infer import Fact

# Tweety is a bird with 90% certainty
Fact("is-bird", ["tweety"], 0.9)

# Room temperature is hot with 75% confidence
Fact("temperature", ["living-room", "hot"], 0.75)

# Alice is tall (100% certain by default)
Fact("is-tall", ["alice"], 1.0)

Why Degrees Matter

Real-world knowledge is rarely absolute:

  • Sensor readings: Temperature sensor reads 78°F → "hot" with degree 0.6
  • Classifications: A platypus is a mammal with degree 0.95 (it's unusual!)
  • Predictions: It will rain tomorrow with confidence 0.7
  • Subjective properties: Alice is tall with degree 0.8 (depends on context)

Rules

Rules define inference patterns that derive new facts from existing ones. They embody domain knowledge and reasoning strategies.

Structure

A rule contains:

  • Name: Identifier for the rule (optional but recommended)
  • Conditions: One or more patterns that must match existing facts
  • Actions: Operations to perform when conditions are satisfied
  • Priority: Execution order (higher priority rules fire first)

Pattern Variables

Rules use variables (prefixed with ?) to match multiple facts:

from fuzzy_infer import Rule

# If ?x is a bird, then ?x can fly
Rule(
    name="birds-can-fly",
    conditions=[
        {"pred": "is-bird", "args": ["?x"]}
    ],
    actions=[
        {"action": "add", "fact": {"pred": "can-fly", "args": ["?x"], "deg": 0.9}}
    ]
)

This single rule applies to all birds: if is-bird(tweety), is-bird(robin), and is-bird(eagle) exist, it infers can-fly for each.

Degree Constraints

Add constraints on degrees for more precise rules:

# Only classify as mammal if certainty > 70%
Rule(
    name="confident-mammals",
    conditions=[
        {
            "pred": "is-mammal",
            "args": ["?x"],
            "deg": "?d",
            "deg-pred": [">", "?d", 0.7]  # Constraint: degree > 0.7
        }
    ],
    actions=[
        {"action": "add", "fact": {"pred": "warm-blooded", "args": ["?x"], "deg": "?d"}}
    ]
)

Degree Calculations

Perform arithmetic on degrees:

# Multiply degrees (conjunction)
{"deg": ["*", "?d", 0.9]}

# Minimum of two degrees (fuzzy AND)
{"deg": ["min", "?d1", "?d2"]}

# Maximum (fuzzy OR)
{"deg": ["max", "?d1", "?d2"]}

# Arithmetic combinations
{"deg": ["+", ["*", "?d", 0.5], 0.3]}

Multiple Conditions

Rules can have multiple conditions (implicitly AND'ed):

Rule(
    name="flying-birds",
    conditions=[
        {"pred": "is-bird", "args": ["?x"], "deg": "?d1"},
        {"pred": "has-wings", "args": ["?x"], "deg": "?d2"},
        {"deg-pred": [">", ["min", "?d1", "?d2"], 0.5]}
    ],
    actions=[
        {"action": "add", "fact": {"pred": "can-fly", "args": ["?x"],
                                   "deg": ["*", ["min", "?d1", "?d2"], 0.85]}}
    ]
)

Inference Engine

The FuzzyInfer engine performs forward-chaining inference, automatically deriving new facts from rules.

Forward Chaining

The inference process:

  1. Match: Find all rule conditions that match current facts
  2. Fire: Execute actions for matched rules (respecting priority)
  3. Update: Add new facts to the knowledge base
  4. Repeat: Continue until no new facts are generated (fixpoint reached)
from fuzzy_infer import FuzzyInfer

# Create engine
inf = FuzzyInfer()

# Add initial facts
inf.add_fact(Fact("is-bird", ["tweety"], 0.9))
inf.add_fact(Fact("has-wings", ["tweety"], 1.0))

# Add rules
inf.add_rule(birds_fly_rule)

# Run inference
inf.run()  # Derives: can-fly(tweety, 0.765)

# Query results
results = inf.query("can-fly", ["tweety"])
print(results)

Iteration Limits

To prevent infinite loops, inference stops after a maximum number of iterations (default: 100):

inf = FuzzyInfer(max_iterations=50)

Fuzzy Logic Operations

Fuzzy AND (T-norms)

Combine multiple conditions using minimum (conservative):

# If bird=0.9 AND wings=1.0, then min(0.9, 1.0) = 0.9
degree = min(bird_degree, wings_degree)

Fuzzy OR (T-conorms)

When multiple rules derive the same fact, use maximum (optimistic):

# Rule 1 infers can-fly(tweety, 0.8)
# Rule 2 infers can-fly(tweety, 0.7)
# Result: can-fly(tweety, 0.8)  ← maximum

FuzzyInfer automatically applies fuzzy OR when adding duplicate facts.

Fuzzy NOT

Negation is computed as complement:

# If is-tall(john, 0.8), then is-short(john, 0.2)
not_degree = 1.0 - degree

Pattern Matching

Variables in conditions bind to concrete values:

# Pattern: parent(?x, ?y) AND parent(?y, ?z)
# Binds: ?x → "alice", ?y → "bob", ?z → "charlie"
# Infers: grandparent("alice", "charlie")

Example: Transitivity

Rule(
    name="grandparent",
    conditions=[
        {"pred": "parent", "args": ["?x", "?y"]},
        {"pred": "parent", "args": ["?y", "?z"]}
    ],
    actions=[
        {"action": "add", "fact": {"pred": "grandparent", "args": ["?x", "?z"]}}
    ]
)

# Given:
# parent(alice, bob)
# parent(bob, charlie)
# Infers:
# grandparent(alice, charlie)

Knowledge Base

The knowledge base stores all facts and rules:

inf = FuzzyInfer()

# Facts storage: {(predicate, tuple(args)) → Fact}
inf.add_fact(Fact("is-bird", ["robin"], 0.9))

# Rules storage: list sorted by priority
inf.add_rule(rule1)  # priority=100
inf.add_rule(rule2)  # priority=50

Fact Deduplication

When adding a fact that already exists, FuzzyInfer applies fuzzy OR (maximum):

inf.add_fact(Fact("is-bird", ["robin"], 0.8))
inf.add_fact(Fact("is-bird", ["robin"], 0.9))
# Result: is-bird(robin, 0.9) ← max(0.8, 0.9)

Querying

Retrieve facts from the knowledge base:

# Query all birds
birds = inf.query("is-bird")

# Query specific animal
robin_facts = inf.query("is-bird", ["robin"])

# Check if fact exists
if inf.query("can-fly", ["tweety"]):
    print("Tweety can fly!")

Actions

Rules can perform three types of actions:

Add Facts

Most common action - add new derived facts:

{"action": "add", "fact": {"pred": "can-fly", "args": ["?x"], "deg": 0.9}}

Modify Facts

Update existing fact degrees:

{"action": "modify", "fact": {"pred": "confidence", "args": ["?x"], "deg": ["*", "?d", 1.2]}}

Retract Facts

Remove facts from the knowledge base:

{"action": "retract", "pred": "temporary", "args": ["?x"]}

Practical Examples

Animal Classification

from fuzzy_infer import FuzzyInfer, Fact, Rule

inf = FuzzyInfer()

# Add observations
inf.add_fact(Fact("has-fur", ["dog"], 1.0))
inf.add_fact(Fact("barks", ["dog"], 0.95))
inf.add_fact(Fact("has-four-legs", ["dog"], 1.0))

# Add classification rules
inf.add_rule(Rule(
    name="mammal-rule",
    conditions=[
        {"pred": "has-fur", "args": ["?x"]},
        {"pred": "has-four-legs", "args": ["?x"]}
    ],
    actions=[
        {"action": "add", "fact": {"pred": "is-mammal", "args": ["?x"], "deg": 0.95}}
    ]
))

inf.add_rule(Rule(
    name="dog-rule",
    conditions=[
        {"pred": "is-mammal", "args": ["?x"]},
        {"pred": "barks", "args": ["?x"], "deg": "?d"},
        {"deg-pred": [">", "?d", 0.7]}
    ],
    actions=[
        {"action": "add", "fact": {"pred": "is-dog", "args": ["?x"], "deg": 0.9}}
    ]
))

# Run inference
inf.run()

# Query results
print(inf.query("is-mammal", ["dog"]))  # [Fact(is-mammal, [dog], 0.95)]
print(inf.query("is-dog", ["dog"]))     # [Fact(is-dog, [dog], 0.855)]

Temperature Control

# Smart home temperature regulation
inf = FuzzyInfer()

# Sensor readings
inf.add_fact(Fact("temperature", ["living-room"], 0.85))  # 85% hot
inf.add_fact(Fact("occupancy", ["living-room"], 0.9))     # 90% occupied

# Control rule
inf.add_rule(Rule(
    name="ac-control",
    conditions=[
        {"pred": "temperature", "args": ["?room"], "deg": "?temp"},
        {"pred": "occupancy", "args": ["?room"], "deg": "?occ"},
        {"deg-pred": [">", "?temp", 0.7]},
        {"deg-pred": [">", "?occ", 0.5]}
    ],
    actions=[
        {"action": "add", "fact": {"pred": "ac-on", "args": ["?room"],
                                   "deg": ["min", "?temp", "?occ"]}}
    ]
))

inf.run()
print(inf.query("ac-on"))  # [Fact(ac-on, [living-room], 0.85)]

Best Practices

1. Use Meaningful Predicates

# Good: descriptive predicates
Fact("is-mammal", ["dog"], 0.9)
Fact("temperature-celsius", ["room-1", 25], 1.0)

# Avoid: cryptic names
Fact("m", ["d"], 0.9)
Fact("t1", ["r1", 25], 1.0)

2. Set Appropriate Degrees

# Good: reflects actual uncertainty
Fact("sensor-reading", ["temp-01"], 0.8)  # Sensor confidence
Fact("prediction", ["rain-tomorrow"], 0.6)  # Weather uncertainty

# Avoid: arbitrary degrees
Fact("is-dog", ["fido"], 0.567)  # Too precise without reason

3. Use Rule Priorities

# Exception rules should have higher priority
Rule(name="general-rule", priority=50, ...)      # General case
Rule(name="exception-rule", priority=100, ...)   # Special case

4. Add Degree Constraints

# Good: only fire rule for high-confidence facts
{"deg-pred": [">", "?d", 0.7]}

# Avoid: processing low-confidence data unnecessarily

5. Name Your Rules

# Good: named rules are easier to debug
Rule(name="birds-fly", ...)

# Avoid: anonymous rules in complex systems
Rule(conditions=..., actions=...)

Summary

  • Facts represent knowledge with degrees of belief (0.0 to 1.0)
  • Rules define inference patterns with conditions and actions
  • Variables (e.g., ?x) enable pattern matching across multiple facts
  • Forward chaining automatically derives new knowledge
  • Fuzzy operations handle uncertainty: AND (min), OR (max), NOT (1 - x)
  • Degrees can be calculated using arithmetic expressions
  • Priority controls rule execution order

Next: Inference Engine - Deep dive into how inference works