Fluent API Guide¶
Master FuzzyInfer's beautiful, Pythonic interface for elegant inference pipelines.
Introduction¶
The Fluent API makes fuzzy inference feel natural in Python:
from fuzzy_infer import fuzzy
# Chain operations elegantly
result = (
fuzzy()
.fact("is-bird", ["robin"], 0.9)
.rule(when="is-bird ?x", then="can-fly ?x 0.85")
.infer()
.query("can-fly")
)
Creating Knowledge Bases¶
Basic Creation¶
from fuzzy_infer import fuzzy, FluentKB
# Using the helper function
kb = fuzzy()
# Or explicitly
kb = FluentKB()
# With initial facts
kb = fuzzy(
("is-mammal", ["dog"]),
("is-mammal", ["cat"], 0.9)
)
Method Chaining¶
Every method returns self for chaining:
kb = (
fuzzy()
.fact("temperature", ["sensor1", 25.5])
.fact("humidity", ["sensor1", 0.65])
.rule(
when=("temperature", "?s", "?t"),
then=("hot", "?s"),
where=lambda b: b["?t"] > 30
)
.infer()
)
Adding Facts¶
Single Facts¶
# Tuple notation
kb.fact("is-bird", ["robin"], 0.9)
# Positional arguments
kb.fact("is-bird", ["eagle"]) # degree=1.0 by default
# Keyword arguments
kb.fact(predicate="can-fly", args=["robin"], degree=0.85)
Multiple Facts¶
# Using facts() method
kb.facts(
("is-mammal", ["dog"]),
("is-mammal", ["cat"], 0.9),
("has-fur", ["dog"], 1.0),
("has-fur", ["cat"], 0.95)
)
# From a list
animal_facts = [
("is-bird", ["robin"], 0.9),
("is-bird", ["eagle"], 1.0)
]
kb.facts(*animal_facts)
Creating Rules¶
Basic Rules¶
# Simple when-then
kb.rule(
when=("is-mammal", "?x"),
then=("is-warm-blooded", "?x")
)
# With degree modification
kb.rule(
when=("is-bird", "?x"),
then=("can-fly", "?x", 0.85)
)
# Named rules
kb.rule(
when=("is-penguin", "?x"),
then=("can-fly", "?x", 0.0),
name="penguin-cannot-fly"
)
Complex Conditions¶
# Multiple AND conditions
kb.rule(
when=[
("has-wings", "?x"),
("has-feathers", "?x"),
("lays-eggs", "?x")
],
then=("is-bird", "?x", 0.95)
)
# OR conditions
kb.rule(
when_any=[
("has-emergency", "?x"),
("priority-high", "?x")
],
then=("process-immediately", "?x")
)
# Mixed AND/OR
kb.rule(
when=[("sensor", "?s", "?value")],
when_any=[
("threshold-exceeded", "?s"),
("manual-override", "?s")
],
then=("trigger-alert", "?s")
)
Constraints and Filters¶
# Using where clause
kb.rule(
when=("age", "?person", "?age"),
then=("is-adult", "?person"),
where=lambda bindings: bindings["?age"] >= 18
)
# Degree constraints
kb.rule(
when=("confidence", "?prediction", degree=">0.8"),
then=("accept", "?prediction")
)
# Complex filtering
kb.rule(
when=[
("temperature", "?sensor", "?temp"),
("humidity", "?sensor", "?humid")
],
then=("uncomfortable", "?sensor"),
where=lambda b: b["?temp"] > 30 and b["?humid"] > 0.7
)
Query DSL¶
Basic Queries¶
# Query all facts of a predicate
all_birds = kb.query("is-bird")
# Query specific arguments
is_robin_bird = kb.query("is-bird", ["robin"])
# Pattern matching
flying_things = kb.query("can-fly", ["?x"])
Chained Queries¶
# Get results and filter
mammals = (
kb.query("is-mammal")
.with_min_degree(0.8)
.sorted_by_degree()
.limit(5)
)
# Transform results
names = kb.query("is-animal", ["?x"]).args(0) # Get first arg of each
Advanced Queries¶
# Multiple patterns
results = kb.where(
("parent", "?x", "?y"),
("parent", "?y", "?z")
).select("?x", "?z") # Get grandparents
# With filters
adults = kb.where(
("person", "?name"),
("age", "?name", "?age"),
filter=lambda b: b["?age"] >= 18
)
Sessions and Context Managers¶
Basic Session¶
from fuzzy_infer import FuzzySession
with FuzzySession() as session:
session.fact("is-bird", ["robin"])
session.rule(
when="is-bird ?x",
then="can-fly ?x"
)
session.infer()
results = session.query("can-fly")
Session Features¶
with FuzzySession(auto_infer=True) as session:
# Auto-infer after each rule
session.fact("temperature", ["room", 25])
session.rule(
when="temperature ?place ?t",
then="comfortable ?place",
where=lambda b: 20 <= b["?t"] <= 26
)
# Inference runs automatically
print(session.query("comfortable"))
Functional Style¶
Lambda Rules¶
# Using lambdas for complex logic
kb.rule(lambda kb:
kb["is-mammal"]["?x"] and kb["is-large"]["?x"]
>> kb.add("is-megafauna", "?x")
)
# Conditional logic
kb.rule(lambda kb, bindings:
kb.add("category", bindings["?x"],
"large" if bindings["?size"] > 100 else "small")
if kb.matches("animal", bindings["?x"])
else None
)
Pipeline Operators¶
from fuzzy_infer import Pipeline
# Create inference pipeline
pipeline = (
Pipeline()
.add_facts(initial_facts)
.add_rules(classification_rules)
.transform(normalize_degrees)
.filter(lambda f: f.degree > 0.5)
.infer()
)
results = pipeline.execute()
Operators and Shortcuts¶
Operator Overloading¶
# Using >> for then
kb.rule(
kb["is-bird"]["?x"] >> kb.add("can-fly", "?x", 0.9)
)
# Using & for AND
kb.rule(
(kb["has-wings"]["?x"] & kb["has-feathers"]["?x"])
>> kb.add("is-bird", "?x")
)
# Using | for OR
kb.rule(
(kb["emergency"]["?x"] | kb["urgent"]["?x"])
>> kb.add("priority-high", "?x")
)
String Shortcuts¶
# Parse rules from strings
kb.rule("is-mammal ?x -> warm-blooded ?x")
kb.rule("is-bird ?x & can-swim ?x -> is-waterfowl ?x 0.9")
# Batch rules
kb.rules("""
is-human ?x -> is-mortal ?x
is-greek ?x & is-human ?x -> is-philosopher ?x 0.8
parent ?x ?y & parent ?y ?z -> grandparent ?x ?z
""")
Fuzzy Expressions¶
Building Expressions¶
from fuzzy_infer import FuzzyExpr
# Create fuzzy expressions
expr = FuzzyExpr("temperature", ">", 30)
# Use in rules
kb.rule(
when=expr.match("?sensor"),
then=("overheating", "?sensor")
)
# Combine expressions
hot = FuzzyExpr("temperature", ">", 30)
humid = FuzzyExpr("humidity", ">", 0.7)
uncomfortable = hot & humid
kb.rule(
when=uncomfortable.match("?place"),
then=("need-ac", "?place")
)
Degree Expressions¶
# Arithmetic on degrees
kb.rule(
when=("confidence", "?x", degree="?c"),
then=("double-check", "?x", degree="1.0 - ?c")
)
# Fuzzy hedges
kb.rule(
when=("tall", "?x", degree="?d"),
then=[
("very-tall", "?x", degree="?d ** 2"), # Very
("somewhat-tall", "?x", degree="?d ** 0.5"), # Somewhat
("extremely-tall", "?x", degree="?d ** 3") # Extremely
]
)
Streaming Support¶
Async Streaming¶
from fuzzy_infer import stream_session
async def process_sensor_stream():
async with stream_session() as stream:
# Add rules
stream.rule(
when="sensor-reading ?id ?value",
then="alert ?id",
where=lambda b: b["?value"] > 100
)
# Process stream
async for reading in sensor_stream:
await stream.add_fact(reading)
alerts = await stream.query("alert")
if alerts:
await notify(alerts)
Batch Processing¶
# Process in batches for efficiency
def process_large_dataset(facts, batch_size=1000):
kb = fuzzy()
kb.add_rules(rules)
for i in range(0, len(facts), batch_size):
batch = facts[i:i+batch_size]
kb.facts(*batch)
kb.infer()
# Process results incrementally
yield kb.query_new() # Get only new facts
kb.clear_new() # Clear new facts marker
Error Handling¶
Validation¶
# Automatic validation
try:
kb.fact("invalid", "not-a-list") # Args must be a list
except ValueError as e:
print(f"Invalid fact: {e}")
# Custom validation
kb.rule(
when="data ?x ?value",
then="valid ?x",
where=lambda b: isinstance(b["?value"], (int, float)),
on_error="skip" # Skip invalid bindings
)
Safe Operations¶
# Safe chaining with defaults
result = (
kb.query("might-not-exist")
.or_default([]) # Return empty list if no results
.first_or_none() # Get first or None
)
# Conditional execution
kb.if_exists("precondition").then_rule(
when="dependent ?x",
then="result ?x"
)
Best Practices¶
1. Use Descriptive Names¶
# Good
kb.rule(
when="has-fever ?patient",
then="needs-medication ?patient",
name="fever-treatment"
)
# Bad
kb.rule(when="f ?x", then="m ?x", name="rule1")
2. Group Related Operations¶
# Group facts by domain
with kb.domain("medical"):
kb.facts(
("patient", ["john"]),
("symptom", ["john", "fever"]),
("symptom", ["john", "cough"])
)
kb.rule(
when=["symptom ?p fever", "symptom ?p cough"],
then="possible-flu ?p"
)
3. Use Type Hints¶
from typing import List
from fuzzy_infer import FluentKB, Fact
def classify_animals(kb: FluentKB, animals: List[str]) -> List[Fact]:
for animal in animals:
kb.fact("animal", [animal])
kb.rule(
when="animal ?x",
then="needs-classification ?x"
)
return kb.infer().query("needs-classification")
Next Steps¶
- Explore the Unix CLI for command-line usage
- See practical Examples
- Check the API Reference for complete details