From Symbolic Reasoning to Rule-Based Systems
A Comprehensive Guide to Logic Libraries in PythonLogic forms the foundation of reasoning, inference, and artificial intelligence. Understanding how to implement logical systems in Python enables you to:
After completing this guide, you will be able to:
Logic programming is a declarative paradigm where you describe what is true, not how to compute it. Programs are written as sets of:
parent(adam, bob).
parent(bob, charlie).
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
?- grandparent(adam, charlie).
% Yes
| Type | Description | Example Libraries | Use Case |
|---|---|---|---|
| Symbolic Logic | Boolean formulas & proofs | SymPy | SAT solving, theorem proving |
| Logic Programming | Relations & facts | Kanren, PyDatalog, Pytholog | Knowledge bases, expert systems |
| Rule-Based Logic | JSON-encoded rules | jsonLogic | Dynamic business rules, APIs |
SymPy is a Python library for symbolic mathematics that includes powerful logic capabilities. It allows you to work with propositional logic, Boolean algebra, and satisfiability checking.
from sympy import symbols, And, Or, Not, Implies, Equivalent, simplify, satisfiable
# Define propositional symbols
P, Q, R = symbols('P Q R')
# Example 1: Modus Ponens
print("=" * 50)
print("Example 1: Modus Ponens")
print("=" * 50)
premise1 = Implies(P, Q) # P → Q
premise2 = P # P
conclusion = Q # Therefore Q
# Check if premises entail conclusion
formula = Implies(And(premise1, premise2), conclusion)
print(f"Formula: {formula}")
print(f"Simplified: {simplify(formula)}") # Should be True (tautology)
print(f"Is valid? {satisfiable(Not(formula)) == False}")
# Example 2: De Morgan's Laws
print("\n" + "=" * 50)
print("Example 2: De Morgan's Laws")
print("=" * 50)
law1 = Equivalent(Not(And(P, Q)), Or(Not(P), Not(Q)))
law2 = Equivalent(Not(Or(P, Q)), And(Not(P), Not(Q)))
print(f"Law 1: ¬(P ∧ Q) ≡ (¬P ∨ ¬Q)")
print(f"Simplified: {simplify(law1)}") # True
print(f"Law 2: ¬(P ∨ Q) ≡ (¬P ∧ ¬Q)")
print(f"Simplified: {simplify(law2)}") # True
# Example 3: SAT Solving
print("\n" + "=" * 50)
print("Example 3: Satisfiability Checking")
print("=" * 50)
formula1 = And(Or(P, Q), Or(Not(P), R), Or(Not(Q), Not(R)))
formula2 = And(P, Not(P)) # Contradiction
print(f"Formula 1: (P ∨ Q) ∧ (¬P ∨ R) ∧ (¬Q ∨ ¬R)")
result1 = satisfiable(formula1)
print(f"Satisfiable? {result1}")
print(f"\nFormula 2: P ∧ ¬P")
result2 = satisfiable(formula2)
print(f"Satisfiable? {result2}") # False (contradiction)
# Example 4: Truth Table Generation
print("\n" + "=" * 50)
print("Example 4: Checking Tautology")
print("=" * 50)
tautology = Or(P, Not(P)) # Law of Excluded Middle
print(f"Formula: P ∨ ¬P")
print(f"Simplified: {simplify(tautology)}")
print(f"Is tautology? {satisfiable(Not(tautology)) == False}")
Kanren (available as logpy in Python) implements relational logic programming,
similar to Prolog. It allows you to define relations and query them declaratively.
from kanren import run, var, Relation, facts, conde, eq
# Define relations
parent = Relation()
male = Relation()
female = Relation()
# Add facts to the knowledge base
facts(parent, ("Adam", "Bob"),
("Adam", "Beth"),
("Bob", "Charlie"),
("Bob", "Claire"),
("Beth", "David"))
facts(male, "Adam", "Bob", "Charlie", "David")
facts(female, "Beth", "Claire")
# Example 1: Simple Query - Who are Adam's children?
print("=" * 50)
print("Example 1: Who are Adam's children?")
print("=" * 50)
x = var()
result = run(0, x, parent("Adam", x)) # 0 means all solutions
print(f"Adam's children: {result}")
# Example 2: Reverse Query - Who is Charlie's parent?
print("\n" + "=" * 50)
print("Example 2: Who is Charlie's parent?")
print("=" * 50)
y = var()
result = run(0, y, parent(y, "Charlie"))
print(f"Charlie's parent: {result}")
# Example 3: Define Grandparent Relation
print("\n" + "=" * 50)
print("Example 3: Grandparent Relation")
print("=" * 50)
def grandparent(x, z):
y = var()
return conde([parent(x, y), parent(y, z)])
a, b = var(), var()
result = run(0, (a, b), grandparent(a, b))
print(f"Grandparent relations: {result}")
# Example 4: Uncle Relation (parent's brother)
print("\n" + "=" * 50)
print("Example 4: Uncle Relation")
print("=" * 50)
def uncle(x, z):
y = var()
return conde([parent(y, z), parent("Adam", x), male(x),
lambda s: s if x != y else None])
u = var()
result = run(0, u, uncle(u, "Charlie"))
print(f"Charlie's uncles: {result}")
# Example 5: Multiple Conditions
print("\n" + "=" * 50)
print("Example 5: All male children")
print("=" * 50)
def male_child(person):
p = var()
return conde([parent(p, person), male(person)])
m = var()
result = run(0, m, male_child(m))
print(f"Male children: {result}")
PyDatalog implements Datalog, a subset of Prolog designed for database queries and recursive reasoning. It provides a SQL-like syntax for logical queries with powerful recursion support.
from pyDatalog import pyDatalog
# Initialize pyDatalog
pyDatalog.clear()
pyDatalog.create_terms('X, Y, Z, parent, ancestor, sibling, cousin, manager, employee, reports_to')
# Example 1: Basic Facts and Queries
print("=" * 50)
print("Example 1: Family Relations")
print("=" * 50)
# Add parent facts
+parent('Adam', 'Bob')
+parent('Adam', 'Beth')
+parent('Bob', 'Charlie')
+parent('Bob', 'Claire')
+parent('Beth', 'David')
+parent('Beth', 'Diana')
# Direct query
print("Adam's children:")
print(parent('Adam', X))
# Example 2: Recursive Ancestor Relation
print("\n" + "=" * 50)
print("Example 2: Recursive Ancestor")
print("=" * 50)
# Define ancestor recursively
ancestor(X, Y) <= parent(X, Y)
ancestor(X, Y) <= parent(X, Z) & ancestor(Z, Y)
# Query all ancestors
print("All of Charlie's ancestors:")
print(ancestor(X, 'Charlie'))
print("\nAll of Adam's descendants:")
print(ancestor('Adam', Y))
# Example 3: Sibling Relation
print("\n" + "=" * 50)
print("Example 3: Siblings")
print("=" * 50)
# Siblings share a parent
sibling(X, Y) <= parent(Z, X) & parent(Z, Y) & (X != Y)
print("Bob's siblings:")
print(sibling('Bob', Y))
print("\nAll sibling pairs:")
result = sibling(X, Y) & (X < Y) # Avoid duplicates
print(result)
# Example 4: Cousin Relation
print("\n" + "=" * 50)
print("Example 4: Cousins")
print("=" * 50)
# Cousins: parents are siblings
cousin(X, Y) <= parent(P1, X) & parent(P2, Y) & sibling(P1, P2)
print("Charlie's cousins:")
print(cousin('Charlie', Y))
# Example 5: Organizational Hierarchy
print("\n" + "=" * 50)
print("Example 5: Corporate Hierarchy")
print("=" * 50)
pyDatalog.create_terms('level')
+manager('CEO', 'VP1')
+manager('CEO', 'VP2')
+manager('VP1', 'Director1')
+manager('VP1', 'Director2')
+manager('Director1', 'Employee1')
+manager('Director1', 'Employee2')
# Reports to (direct or indirect)
reports_to(X, Y) <= manager(Y, X)
reports_to(X, Y) <= manager(Z, X) & reports_to(Z, Y)
print("Who reports to CEO (directly or indirectly)?")
print(reports_to(X, 'CEO'))
# Example 6: Counting with Aggregation
print("\n" + "=" * 50)
print("Example 6: Count Descendants")
print("=" * 50)
(employee[Y] == len_(X)) <= ancestor(Y, X)
print("Number of descendants for each person:")
print(employee[Y] == X)
Pytholog is a lightweight Prolog interpreter written in Python. It provides a simple, intuitive interface for building knowledge bases and performing logical queries.
import pytholog as pl
# Example 1: Family Knowledge Base
print("=" * 50)
print("Example 1: Family Relations")
print("=" * 50)
kb = pl.KnowledgeBase("family")
kb([
"parent(adam, bob)",
"parent(adam, beth)",
"parent(bob, charlie)",
"parent(bob, claire)",
"parent(beth, david)",
"male(adam)",
"male(bob)",
"male(charlie)",
"male(david)",
"female(beth)",
"female(claire)",
# Rules
"grandparent(X, Z) :- parent(X, Y), parent(Y, Z)",
"father(X, Y) :- parent(X, Y), male(X)",
"mother(X, Y) :- parent(X, Y), female(X)",
"sibling(X, Y) :- parent(Z, X), parent(Z, Y), X \\= Y",
"brother(X, Y) :- sibling(X, Y), male(X)",
"sister(X, Y) :- sibling(X, Y), female(X)"
])
# Query grandparents
print("Query: grandparent(adam, X)")
result = kb.query(pl.Expr("grandparent(adam, X)"))
print(f"Result: {result}")
# Query fathers
print("\nQuery: father(X, charlie)")
result = kb.query(pl.Expr("father(X, charlie)"))
print(f"Result: {result}")
# Example 2: Animal Classification Expert System
print("\n" + "=" * 50)
print("Example 2: Animal Classification")
print("=" * 50)
animal_kb = pl.KnowledgeBase("animals")
animal_kb([
# Facts about animals
"has_fur(dog)",
"has_fur(cat)",
"has_fur(lion)",
"has_feathers(eagle)",
"has_feathers(penguin)",
"has_feathers(parrot)",
"can_fly(eagle)",
"can_fly(parrot)",
"lives_in_water(penguin)",
"lives_in_water(shark)",
"lives_in_water(salmon)",
"has_gills(shark)",
"has_gills(salmon)",
# Classification rules
"mammal(X) :- has_fur(X)",
"bird(X) :- has_feathers(X)",
"fish(X) :- has_gills(X), lives_in_water(X)",
"flying_bird(X) :- bird(X), can_fly(X)",
"flightless_bird(X) :- bird(X), \\+ can_fly(X)"
])
# Classify animals
print("Query: What is a mammal?")
result = animal_kb.query(pl.Expr("mammal(X)"))
print(f"Mammals: {result}")
print("\nQuery: What is a flying bird?")
result = animal_kb.query(pl.Expr("flying_bird(X)"))
print(f"Flying birds: {result}")
print("\nQuery: What is a fish?")
result = animal_kb.query(pl.Expr("fish(X)"))
print(f"Fish: {result}")
# Example 3: Medical Diagnosis System
print("\n" + "=" * 50)
print("Example 3: Simple Medical Diagnosis")
print("=" * 50)
medical_kb = pl.KnowledgeBase("medical")
medical_kb([
# Patient symptoms
"symptom(john, fever)",
"symptom(john, cough)",
"symptom(john, fatigue)",
"symptom(mary, headache)",
"symptom(mary, nausea)",
"symptom(alice, fever)",
"symptom(alice, rash)",
"symptom(alice, sore_throat)",
# Diagnosis rules
"diagnosis(Patient, flu) :- symptom(Patient, fever), symptom(Patient, cough), symptom(Patient, fatigue)",
"diagnosis(Patient, migraine) :- symptom(Patient, headache), symptom(Patient, nausea)",
"diagnosis(Patient, strep_throat) :- symptom(Patient, fever), symptom(Patient, sore_throat)"
])
# Diagnose patients
print("Query: diagnosis(john, Disease)")
result = medical_kb.query(pl.Expr("diagnosis(john, Disease)"))
print(f"John's diagnosis: {result}")
print("\nQuery: diagnosis(alice, Disease)")
result = medical_kb.query(pl.Expr("diagnosis(alice, Disease)"))
print(f"Alice's diagnosis: {result}")
print("\nQuery: Who has the flu?")
result = medical_kb.query(pl.Expr("diagnosis(Patient, flu)"))
print(f"Patients with flu: {result}")
# Example 4: Course Prerequisites
print("\n" + "=" * 50)
print("Example 4: Course Prerequisites")
print("=" * 50)
course_kb = pl.KnowledgeBase("courses")
course_kb([
# Direct prerequisites
"prerequisite(se444, se363)",
"prerequisite(se363, coe202)",
"prerequisite(se444, math201)",
"prerequisite(se465, se363)",
"prerequisite(se465, se444)",
# Student completed courses
"completed(ahmad, coe202)",
"completed(ahmad, math201)",
"completed(ahmad, se363)",
"completed(sara, coe202)",
"completed(sara, math201)",
# Rules
"can_take(Student, Course) :- prerequisite(Course, Prereq), completed(Student, Prereq)",
"all_prereqs_met(Student, Course) :- \\+ (prerequisite(Course, Prereq), \\+ completed(Student, Prereq))"
])
print("Query: What can Ahmad take?")
result = course_kb.query(pl.Expr("can_take(ahmad, Course)"))
print(f"Ahmad can take: {result}")
print("\nQuery: Can Ahmad take SE444?")
result = course_kb.query(pl.Expr("can_take(ahmad, se444)"))
print(f"Result: {result}")
jsonLogic is a way to write portable logic rules in JSON format. This makes rules shareable between different programming languages, databases, and APIs.
from json_logic import jsonLogic
import json
# Example 1: Simple Eligibility Check
print("=" * 50)
print("Example 1: Loan Eligibility")
print("=" * 50)
# Rule: Age > 18 AND Income > 30000
loan_rule = {
"and": [
{">": [{"var": "age"}, 18]},
{">": [{"var": "income"}, 30000]}
]
}
# Test cases
applicant1 = {"age": 25, "income": 45000}
applicant2 = {"age": 17, "income": 50000}
applicant3 = {"age": 30, "income": 25000}
print(f"Rule: {json.dumps(loan_rule, indent=2)}")
print(f"\nApplicant 1 (age=25, income=45000): {jsonLogic(loan_rule, applicant1)}")
print(f"Applicant 2 (age=17, income=50000): {jsonLogic(loan_rule, applicant2)}")
print(f"Applicant 3 (age=30, income=25000): {jsonLogic(loan_rule, applicant3)}")
# Example 2: Complex Discount Calculator
print("\n" + "=" * 50)
print("Example 2: Dynamic Discount Rules")
print("=" * 50)
# Tiered discount system
discount_rule = {
"if": [
{">": [{"var": "total"}, 1000]},
20, # 20% discount for orders > 1000
{">": [{"var": "total"}, 500]},
15, # 15% discount for orders > 500
{">": [{"var": "total"}, 100]},
10, # 10% discount for orders > 100
0 # No discount otherwise
]
}
orders = [
{"total": 1500},
{"total": 750},
{"total": 150},
{"total": 50}
]
print(f"Discount Rule: {json.dumps(discount_rule, indent=2)}")
for order in orders:
discount = jsonLogic(discount_rule, order)
print(f"Order total ${order['total']}: {discount}% discount")
# Example 3: Healthcare Risk Assessment
print("\n" + "=" * 50)
print("Example 3: Healthcare Risk Score")
print("=" * 50)
# Risk score: multiple factors
risk_rule = {
"+": [
# Age factor (0-30 points)
{"if": [{">": [{"var": "age"}, 60]}, 30,
{">": [{"var": "age"}, 40]}, 15, 0]},
# BMI factor (0-20 points)
{"if": [{">": [{"var": "bmi"}, 30]}, 20,
{">": [{"var": "bmi"}, 25]}, 10, 0]},
# Smoking (25 points)
{"if": [{"var": "smoker"}, 25, 0]},
# Diabetes (15 points)
{"if": [{"var": "diabetes"}, 15, 0]}
]
}
patients = [
{"age": 65, "bmi": 32, "smoker": True, "diabetes": True},
{"age": 35, "bmi": 24, "smoker": False, "diabetes": False},
{"age": 50, "bmi": 28, "smoker": True, "diabetes": False}
]
print("Risk Assessment Rule:")
for i, patient in enumerate(patients, 1):
risk_score = jsonLogic(risk_rule, patient)
risk_level = "HIGH" if risk_score > 50 else "MEDIUM" if risk_score > 25 else "LOW"
print(f"Patient {i}: Score = {risk_score} ({risk_level})")
print(f" Age={patient['age']}, BMI={patient['bmi']}, "
f"Smoker={patient['smoker']}, Diabetes={patient['diabetes']}")
# Example 4: Access Control Rules
print("\n" + "=" * 50)
print("Example 4: Access Control System")
print("=" * 50)
# Rule: Can edit if (owner OR admin) AND not_locked
access_rule = {
"and": [
{
"or": [
{"==": [{"var": "role"}, "admin"]},
{"==": [{"var": "user_id"}, {"var": "owner_id"}]}
]
},
{"!": {"var": "locked"}}
]
}
# Test scenarios
scenarios = [
{"user_id": 101, "owner_id": 101, "role": "user", "locked": False},
{"user_id": 102, "owner_id": 101, "role": "admin", "locked": False},
{"user_id": 101, "owner_id": 101, "role": "user", "locked": True},
{"user_id": 103, "owner_id": 101, "role": "user", "locked": False}
]
print(f"Access Rule: {json.dumps(access_rule, indent=2)}")
for i, scenario in enumerate(scenarios, 1):
can_edit = jsonLogic(access_rule, scenario)
print(f"Scenario {i}: Can edit = {can_edit}")
print(f" {scenario}")
# Example 5: Product Recommendation
print("\n" + "=" * 50)
print("Example 5: Product Recommendation Logic")
print("=" * 50)
# Recommend premium if: (income > 80000 AND age > 25) OR lifetime_value > 50000
recommend_premium_rule = {
"or": [
{
"and": [
{">": [{"var": "income"}, 80000]},
{">": [{"var": "age"}, 25]}
]
},
{">": [{"var": "lifetime_value"}, 50000]}
]
}
customers = [
{"income": 90000, "age": 30, "lifetime_value": 10000},
{"income": 60000, "age": 35, "lifetime_value": 60000},
{"income": 50000, "age": 22, "lifetime_value": 5000}
]
print("Premium Recommendation Rule:")
for i, customer in enumerate(customers, 1):
recommend = jsonLogic(recommend_premium_rule, customer)
print(f"Customer {i}: Recommend Premium = {recommend}")
print(f" Income=${customer['income']}, Age={customer['age']}, "
f"LTV=${customer['lifetime_value']}")
# Example 6: Array Operations
print("\n" + "=" * 50)
print("Example 6: Working with Arrays")
print("=" * 50)
# Check if any order is high priority
priority_rule = {
"some": [
{"var": "orders"},
{">": [{"var": "priority"}, 8]}
]
}
customer_data = {
"orders": [
{"id": 1, "priority": 5},
{"id": 2, "priority": 9},
{"id": 3, "priority": 3}
]
}
has_high_priority = jsonLogic(priority_rule, customer_data)
print(f"Has high priority order: {has_high_priority}")
# Filter high-value items
filter_rule = {
"filter": [
{"var": "items"},
{">": [{"var": "price"}, 100]}
]
}
cart_data = {
"items": [
{"name": "Laptop", "price": 1200},
{"name": "Mouse", "price": 25},
{"name": "Monitor", "price": 350},
{"name": "Keyboard", "price": 80}
]
}
expensive_items = jsonLogic(filter_rule, cart_data)
print(f"\nExpensive items (>$100): {expensive_items}")
| Library | Type | Syntax | Strength | Best For | Learning Curve |
|---|---|---|---|---|---|
| SymPy | Symbolic Logic | Python | Mathematical proofs | SAT, tautologies, teaching | Low |
| Kanren | Logic Programming | Declarative | Relational reasoning | AI reasoning, constraints | Medium |
| PyDatalog | Datalog | SQL-like | Recursive queries | Knowledge bases, graphs | Medium |
| Pytholog | Prolog | Declarative | Rule-based AI | Expert systems, diagnosis | Medium-High |
| jsonLogic | Rule Engine | JSON | Dynamic evaluation | Business rules, APIs | Low |
You can combine multiple libraries for powerful hybrid systems:
# Combining SymPy and jsonLogic for a complete system
from sympy import symbols, And, Or, Not, Implies, simplify
from json_logic import jsonLogic
# Step 1: Verify rule correctness with SymPy
print("Step 1: Verifying rule correctness with SymPy")
print("=" * 50)
P, Q, R = symbols('P Q R')
# Rule: If (P AND Q) then R
symbolic_rule = Implies(And(P, Q), R)
print(f"Symbolic rule: {symbolic_rule}")
print(f"Simplified: {simplify(symbolic_rule)}")
# Check if it's logically valid
test_formula = Or(Not(And(P, Q)), R) # Equivalent form
print(f"Equivalent form: {test_formula}")
print(f"Are they equivalent? {simplify(Implies(symbolic_rule, test_formula)) == True}")
# Step 2: Convert to jsonLogic for runtime use
print("\n\nStep 2: Converting to jsonLogic for runtime")
print("=" * 50)
json_rule = {
"if": [
{"and": [{"var": "P"}, {"var": "Q"}]},
{"var": "R"},
True
]
}
# Test cases
test_cases = [
{"P": True, "Q": True, "R": True},
{"P": True, "Q": True, "R": False},
{"P": False, "Q": True, "R": False},
]
for i, case in enumerate(test_cases, 1):
result = jsonLogic(json_rule, case)
print(f"Test case {i}: {case} => {result}")
print("\n✓ Rule verified symbolically and ready for production use!")
Once you're comfortable with the fundamentals, explore these advanced areas:
Google OR-Tools and python-constraint provide powerful constraint satisfaction problem (CSP) solving.
RDFLib and Neo4j enable semantic reasoning over graph-structured knowledge.
ProbLog and PyMC combine logic with probability for uncertain reasoning.
Combining deep learning with symbolic reasoning for explainable AI.
# Install all libraries covered in this guide
pip install sympy
pip install logpy # Kanren
pip install pyDatalog
pip install pytholog
pip install json-logic
# Optional: Advanced libraries
pip install ortools # Constraint programming
pip install python-constraint
pip install rdflib # Knowledge graphs
pip install problog # Probabilistic logic