-
-
Notifications
You must be signed in to change notification settings - Fork 50.5k
Expand file tree
/
Copy pathttentails.py
More file actions
79 lines (63 loc) · 2.8 KB
/
ttentails.py
File metadata and controls
79 lines (63 loc) · 2.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"""
TT-ENTAILS Algorithm (Propositional Logic)
Reference: [Russell & Norvig, Artificial Intelligence: A Modern Approach, Ch. 7](https://aima.cs.berkeley.edu/)
Wikipedia: [Entailment](https://en.wikipedia.org/wiki/Entailment)
This algorithm checks if a knowledge base (KB) entails a query sentence (a)
using truth tables. Returns True if KB entails a, False otherwise.
"""
import itertools
import ast
import operator
OPS = {ast.And: operator.and_, ast.Or: operator.or_, ast.Not: operator.not_}
def safe_eval(expr: str, model: dict[str, bool]) -> bool:
"""Safely evaluate propositional logic expression using ast."""
tree = ast.parse(expr, mode="eval")
def _eval(node):
if isinstance(node, ast.Expression):
return _eval(node.body)
if isinstance(node, ast.Name):
return model[node.id]
if isinstance(node, ast.Constant): # True / False
return node.value
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not):
return OPS[ast.Not](_eval(node.operand))
if isinstance(node, ast.BoolOp):
if isinstance(node.op, ast.And):
return all(_eval(v) for v in node.values)
if isinstance(node.op, ast.Or):
return any(_eval(v) for v in node.values)
raise ValueError(f"Unsupported expression: {expr}")
return _eval(tree)
def tt_entails(kb: list[str], query: str, symbols: list[str]) -> bool:
"""
Check if the knowledge base entails the query using truth tables.
Args:
kb (List[str]): List of propositional sentences in KB as strings
query (str): Query sentence to test entailment
symbols (List[str]): List of all propositional symbols used
Returns:
bool: True if KB entails query, False otherwise
Example:
tt_entails(["P or Q"], "Q", ["P","Q"])
"""
for values in itertools.product([True, False], repeat=len(symbols)):
model: dict[str, bool] = dict(zip(symbols, values))
# Check if KB is true under this model
# # If query is false in this model, KB does not entail query
if all(safe_eval(sentence, model) for sentence in kb) and not safe_eval(
query, model
):
return False
return True
# Example usage
if __name__ == "__main__":
# Example 1: KB entails query → should return True
symbols = ["P", "Q"]
kb = ["P or Q", "not P or Q"] # KB says P or Q is True, and not P or Q is True
query = "Q" # Query: Is Q True?
print("Does KB entail query? : ", tt_entails(kb, query, symbols))
# Example 2: KB does NOT entail query → should return False
symbols2 = ["P", "Q"]
kb2 = ["P"] # KB says only P is True
query2 = "Q" # Query asks if Q is True
print("Does KB2 entail query2? : ", tt_entails(kb2, query2, symbols2))