|
|
- 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())
|