Initial commit.
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
import itertools
|
||||
|
||||
|
||||
class Sentence():
|
||||
|
||||
def evaluate(self, model):
|
||||
"""Evaluates the logical sentence."""
|
||||
raise Exception("nothing to evaluate")
|
||||
|
||||
def formula(self):
|
||||
"""Returns string formula representing logical sentence."""
|
||||
return ""
|
||||
|
||||
def symbols(self):
|
||||
"""Returns a set of all symbols in the logical sentence."""
|
||||
return set()
|
||||
|
||||
@classmethod
|
||||
def validate(cls, sentence):
|
||||
if not isinstance(sentence, Sentence):
|
||||
raise TypeError("must be a logical sentence")
|
||||
|
||||
@classmethod
|
||||
def parenthesize(cls, s):
|
||||
"""Parenthesizes an expression if not already parenthesized."""
|
||||
def balanced(s):
|
||||
"""Checks if a string has balanced parentheses."""
|
||||
count = 0
|
||||
for c in s:
|
||||
if c == "(":
|
||||
count += 1
|
||||
elif c == ")":
|
||||
if count <= 0:
|
||||
return False
|
||||
count -= 1
|
||||
return count == 0
|
||||
if not len(s) or s.isalpha() or (
|
||||
s[0] == "(" and s[-1] == ")" and balanced(s[1:-1])
|
||||
):
|
||||
return s
|
||||
else:
|
||||
return f"({s})"
|
||||
|
||||
|
||||
class Symbol(Sentence):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Symbol) and self.name == other.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("symbol", self.name))
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
def evaluate(self, model):
|
||||
try:
|
||||
return bool(model[self.name])
|
||||
except KeyError:
|
||||
raise Exception(f"variable {self.name} not in model")
|
||||
|
||||
def formula(self):
|
||||
return self.name
|
||||
|
||||
def symbols(self):
|
||||
return {self.name}
|
||||
|
||||
|
||||
class Not(Sentence):
|
||||
def __init__(self, operand):
|
||||
Sentence.validate(operand)
|
||||
self.operand = operand
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Not) and self.operand == other.operand
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("not", hash(self.operand)))
|
||||
|
||||
def __repr__(self):
|
||||
return f"Not({self.operand})"
|
||||
|
||||
def evaluate(self, model):
|
||||
return not self.operand.evaluate(model)
|
||||
|
||||
def formula(self):
|
||||
return "¬" + Sentence.parenthesize(self.operand.formula())
|
||||
|
||||
def symbols(self):
|
||||
return self.operand.symbols()
|
||||
|
||||
|
||||
class And(Sentence):
|
||||
def __init__(self, *conjuncts):
|
||||
for conjunct in conjuncts:
|
||||
Sentence.validate(conjunct)
|
||||
self.conjuncts = list(conjuncts)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, And) and self.conjuncts == other.conjuncts
|
||||
|
||||
def __hash__(self):
|
||||
return hash(
|
||||
("and", tuple(hash(conjunct) for conjunct in self.conjuncts))
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
conjunctions = ", ".join(
|
||||
[str(conjunct) for conjunct in self.conjuncts]
|
||||
)
|
||||
return f"And({conjunctions})"
|
||||
|
||||
def add(self, conjunct):
|
||||
Sentence.validate(conjunct)
|
||||
self.conjuncts.append(conjunct)
|
||||
|
||||
def evaluate(self, model):
|
||||
return all(conjunct.evaluate(model) for conjunct in self.conjuncts)
|
||||
|
||||
def formula(self):
|
||||
if len(self.conjuncts) == 1:
|
||||
return self.conjuncts[0].formula()
|
||||
return " ∧ ".join([Sentence.parenthesize(conjunct.formula())
|
||||
for conjunct in self.conjuncts])
|
||||
|
||||
def symbols(self):
|
||||
return set.union(*[conjunct.symbols() for conjunct in self.conjuncts])
|
||||
|
||||
|
||||
class Or(Sentence):
|
||||
def __init__(self, *disjuncts):
|
||||
for disjunct in disjuncts:
|
||||
Sentence.validate(disjunct)
|
||||
self.disjuncts = list(disjuncts)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Or) and self.disjuncts == other.disjuncts
|
||||
|
||||
def __hash__(self):
|
||||
return hash(
|
||||
("or", tuple(hash(disjunct) for disjunct in self.disjuncts))
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
disjuncts = ", ".join([str(disjunct) for disjunct in self.disjuncts])
|
||||
return f"Or({disjuncts})"
|
||||
|
||||
def evaluate(self, model):
|
||||
return any(disjunct.evaluate(model) for disjunct in self.disjuncts)
|
||||
|
||||
def formula(self):
|
||||
if len(self.disjuncts) == 1:
|
||||
return self.disjuncts[0].formula()
|
||||
return " ∨ ".join([Sentence.parenthesize(disjunct.formula())
|
||||
for disjunct in self.disjuncts])
|
||||
|
||||
def symbols(self):
|
||||
return set.union(*[disjunct.symbols() for disjunct in self.disjuncts])
|
||||
|
||||
|
||||
class Implication(Sentence):
|
||||
def __init__(self, antecedent, consequent):
|
||||
Sentence.validate(antecedent)
|
||||
Sentence.validate(consequent)
|
||||
self.antecedent = antecedent
|
||||
self.consequent = consequent
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, Implication)
|
||||
and self.antecedent == other.antecedent
|
||||
and self.consequent == other.consequent)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("implies", hash(self.antecedent), hash(self.consequent)))
|
||||
|
||||
def __repr__(self):
|
||||
return f"Implication({self.antecedent}, {self.consequent})"
|
||||
|
||||
def evaluate(self, model):
|
||||
return ((not self.antecedent.evaluate(model))
|
||||
or self.consequent.evaluate(model))
|
||||
|
||||
def formula(self):
|
||||
antecedent = Sentence.parenthesize(self.antecedent.formula())
|
||||
consequent = Sentence.parenthesize(self.consequent.formula())
|
||||
return f"{antecedent} => {consequent}"
|
||||
|
||||
def symbols(self):
|
||||
return set.union(self.antecedent.symbols(), self.consequent.symbols())
|
||||
|
||||
|
||||
class Biconditional(Sentence):
|
||||
def __init__(self, left, right):
|
||||
Sentence.validate(left)
|
||||
Sentence.validate(right)
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, Biconditional)
|
||||
and self.left == other.left
|
||||
and self.right == other.right)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(("biconditional", hash(self.left), hash(self.right)))
|
||||
|
||||
def __repr__(self):
|
||||
return f"Biconditional({self.left}, {self.right})"
|
||||
|
||||
def evaluate(self, model):
|
||||
return ((self.left.evaluate(model)
|
||||
and self.right.evaluate(model))
|
||||
or (not self.left.evaluate(model)
|
||||
and not self.right.evaluate(model)))
|
||||
|
||||
def formula(self):
|
||||
left = Sentence.parenthesize(str(self.left))
|
||||
right = Sentence.parenthesize(str(self.right))
|
||||
return f"{left} <=> {right}"
|
||||
|
||||
def symbols(self):
|
||||
return set.union(self.left.symbols(), self.right.symbols())
|
||||
|
||||
|
||||
def model_check(knowledge, query):
|
||||
"""Checks if knowledge base entails query."""
|
||||
|
||||
def check_all(knowledge, query, symbols, model):
|
||||
"""Checks if knowledge base entails query, given a particular model."""
|
||||
|
||||
# If model has an assignment for each symbol
|
||||
if not symbols:
|
||||
|
||||
# If knowledge base is true in model, then query must also be true
|
||||
if knowledge.evaluate(model):
|
||||
return query.evaluate(model)
|
||||
return True
|
||||
else:
|
||||
|
||||
# Choose one of the remaining unused symbols
|
||||
remaining = symbols.copy()
|
||||
p = remaining.pop()
|
||||
|
||||
# Create a model where the symbol is true
|
||||
model_true = model.copy()
|
||||
model_true[p] = True
|
||||
|
||||
# Create a model where the symbol is false
|
||||
model_false = model.copy()
|
||||
model_false[p] = False
|
||||
|
||||
# Ensure entailment holds in both models
|
||||
return (check_all(knowledge, query, remaining, model_true) and
|
||||
check_all(knowledge, query, remaining, model_false))
|
||||
|
||||
# Get all symbols in both knowledge and query
|
||||
symbols = set.union(knowledge.symbols(), query.symbols())
|
||||
|
||||
# Check that knowledge entails query
|
||||
return check_all(knowledge, query, symbols, dict())
|
||||
@@ -0,0 +1,80 @@
|
||||
from logic import *
|
||||
|
||||
AKnight = Symbol("A is a Knight")
|
||||
AKnave = Symbol("A is a Knave")
|
||||
|
||||
BKnight = Symbol("B is a Knight")
|
||||
BKnave = Symbol("B is a Knave")
|
||||
|
||||
CKnight = Symbol("C is a Knight")
|
||||
CKnave = Symbol("C is a Knave")
|
||||
|
||||
# Puzzle 0
|
||||
# A says "I am both a knight and a knave."
|
||||
knowledge0 = And(
|
||||
Or(AKnave, AKnight),
|
||||
And(AKnave, Not(AKnight)),
|
||||
Implication(And(AKnight, AKnave), AKnave)
|
||||
)
|
||||
|
||||
# Puzzle 1
|
||||
# A says "We are both knaves."
|
||||
# B says nothing.
|
||||
knowledge1 = And(
|
||||
Or(AKnave, AKnight),
|
||||
Or(BKnave, BKnight),
|
||||
Or(And(AKnave, BKnight), And(AKnave, BKnave)),
|
||||
Implication(And(AKnave, BKnave), And(AKnave, BKnight))
|
||||
)
|
||||
|
||||
# Puzzle 2
|
||||
# A says "We are the same kind."
|
||||
# B says "We are of different kinds."
|
||||
knowledge2 = And(
|
||||
Or(And(AKnave, BKnave), And(AKnight, BKnight), And(AKnave, BKnight)),
|
||||
Biconditional(AKnave, Not(AKnight)),
|
||||
Biconditional(BKnave, Not(BKnight)),
|
||||
Implication(AKnave, Not(And(AKnave, BKnave))),
|
||||
Implication(BKnight, And(AKnave, BKnight))
|
||||
)
|
||||
|
||||
# Puzzle 3
|
||||
# A says either "I am a knight." or "I am a knave.", but you don't know which.
|
||||
# B says "A said 'I am a knave'."
|
||||
# B says "C is a knave."
|
||||
# C says "A is a knight."
|
||||
knowledge3 = And(
|
||||
Or(AKnave, AKnight),
|
||||
Or(BKnave, BKnight),
|
||||
Or(CKnave, CKnight),
|
||||
Biconditional(AKnave, Not(AKnight)),
|
||||
Biconditional(BKnave, Not(BKnight)),
|
||||
Biconditional(CKnave, Not(CKnight)),
|
||||
|
||||
Implication(AKnave, Not(AKnave)),
|
||||
Implication(BKnight, And(AKnave, CKnave)),
|
||||
Implication(BKnave, And(AKnight, CKnight)),
|
||||
Implication(CKnight, And(AKnight, BKnave))
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
symbols = [AKnight, AKnave, BKnight, BKnave, CKnight, CKnave]
|
||||
puzzles = [
|
||||
("Puzzle 0", knowledge0),
|
||||
("Puzzle 1", knowledge1),
|
||||
("Puzzle 2", knowledge2),
|
||||
("Puzzle 3", knowledge3)
|
||||
]
|
||||
for puzzle, knowledge in puzzles:
|
||||
print(puzzle)
|
||||
if len(knowledge.conjuncts) == 0:
|
||||
print(" Not yet implemented.")
|
||||
else:
|
||||
for symbol in symbols:
|
||||
if model_check(knowledge, symbol):
|
||||
print(f" {symbol}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user