Propositional logic, also known as propositional calculus or sentential logic, is a branch of logic that deals with propositions and their relationships through logical connectives. It is a foundational system in formal logic and is used to analyze and represent logical statements.
Problems statements¶
How to represent logical statements using symbols and connectives? And print statements from them?
How to evaluate the truth value of complex logical expressions?
How to construct relation chains like parent, grandparent etc from basic relations like parent and child? [BONUS]
Basic building block¶
Just printing out what a logic does...
First let’s define a simple class for storing our statements.
class Statement:
def __init__(self, subject, predicate, singular=True):
self.subject = subject
self.predicate = predicate
self.singular = singularNow we will define our actual relation class with methods to pring natural language statements from logic...
class Relation:
def __init__(self):
self.map = {}
def add(self, statement, subject, predicate, singular=True):
self.map[statement] = Statement(subject, predicate, singular)
def print_statement(self, statement):
if self.map[statement].singular:
print(self.map[statement].subject, " is ", self.map[statement].predicate, end="")
else:
print(self.map[statement].subject, " are ", self.map[statement].predicate, end="")
def print_neg_statement(self, statement):
if self.map[statement].singular:
print(self.map[statement].subject, " is not ", self.map[statement].predicate, end="")
else:
print(self.map[statement].subject, " are not ", self.map[statement].predicate, end="")
def build_statement(self, logic):
"""
& = AND
| = OR
! = NOT
~ = IMPLIES
* = IF AND ONLY IF
A = EVERY
E = SOME
"""
logic = logic.replace(" ", "") # no space
logic = logic.replace(",", "") # no comma
neg = False
only_sub = False
for i in range(len(logic)):
if logic[i] == '!':
neg = True
elif logic[i] == '&':
print(" and ", end="")
elif logic[i] == '|':
print(" or ", end="")
elif logic[i] == '*':
print(" if and only if, ", end="")
elif logic[i] == '~':
print(" implies, ", end="")
elif logic[i] == 'A':
print(" For all ", end="")
only_sub = True
elif logic[i] == 'E':
print(" For some ", end="")
only_sub = True
elif logic[i] in ['(', ')', ',']:
continue
else:
if only_sub:
print(self.map[logic[i]].subject, end=", ")
only_sub = False
elif neg:
self.print_neg_statement(logic[i])
neg = False
else:
self.print_statement(logic[i])
print(".")Let’s test our class,
relation = Relation()
relation.add("s", "students", "brilliant", singular=False)
relation.add("a", "Real CR", "student")
relation.add("b", "Real CR", "lazy")
relation.build_statement("A(s) s")
relation.build_statement("a & !b ~ a") For all students, students are brilliant.
Real CR is student and Real CR is not lazy implies, Real CR is student.
Truth Table¶
Let’s build truth tables for basic logical operations. We need to draw tables again and again, so let’s create a function to do that.
def draw_table(li, *args):
length = len(" | ".join(args))
print("-" * length)
print(" | ".join(args))
print("-" * length)
for row in li:
print(" | ".join(str(i) for i in row))
# print(" | ".join(row))
print("-" * length)
draw_table([["1", "1", "1"],
["1", "0", "0"],
["0", "1", "0"],
["0", "0", "0"]], "A", "B", "A AND B")---------------
A | B | A AND B
---------------
1 | 1 | 1
1 | 0 | 0
0 | 1 | 0
0 | 0 | 0
---------------
An another function to evaluate the result,
def evaluate_expression(a, b, ex):
"""
& = AND
| = OR
! = NOT
~ = IMPLIES
* = IF AND ONLY IF
"""
if (ex == '!'):
return 0 if a == 1 else 1
elif (ex == '&'):
return 1 if a == 1 and b == 1 else 0
elif (ex == '|'):
return 1 if a == 1 or b == 1 else 0
elif (ex == '~'):
return 0 if a == 1 and b == 0 else 1
elif (ex == '*'):
return 1 if (a == 1 and b == 1) or (a == 0 and b == 0) else 0[OPTIONAL] With our evaluate_expression and draw_table function, let’s draw some truth tables...
N = 2 # Number of variables
arr = [list(map(int, bin(i)[2:].zfill(N))) for i in range(2**N)]
for i in arr:
i.append(int(str(evaluate_expression(i[0], i[1], '*'))))
draw_table(arr, "A", "B", "ONLY IF")---------------
A | B | ONLY IF
---------------
0 | 0 | 1
0 | 1 | 0
1 | 0 | 0
1 | 1 | 1
---------------
Extended Proposition!¶
Let’s inherit our previous Relation class and extend it to evaluate complex logical expressions.
from collections import deque
class Relation(Relation):
def __init__(self):
super().__init__()
self.operand_val = {}
def map_operands(self, op, val):
self.operand_val[op] = val
def evaluate_expression(self, exp):
"""
& = AND
| = OR
! = NOT
~ = IMPLIES
* = IF AND ONLY IF
"""
exp = exp.replace(" ", "") # no space
exp = exp.replace(",", "") # no comma
operators = deque()
operands = deque()
negative = False
for i in exp:
if i in ['&', '|', '~', '*']:
operators.append(i)
elif i == '!':
negative = True
else:
if negative:
operands.append(evaluate_expression(self.operand_val[i], 0, '!'))
negative = False
else:
operands.append(i)
operands = deque(self.operand_val[i] if (i != 0 and i != 1) else i for i in operands )
while operators:
op = operators.popleft()
a = operands.popleft()
b = operands.popleft()
operands.appendleft(evaluate_expression(a, b, op))
return operands.popleft()And let’s test it,
relation = Relation()
relation.add("a", "sensei", "student")
relation.add("b", "sensei", "brilliant")
relation.map_operands("a", 1)
relation.map_operands("b", 0)
print(relation.evaluate_expression("a | b & !a ~ b"))1
And now’s the fun part! Let’s draw truth tables for a and b,
array = [list(map(int, bin(i)[2:].zfill(2))) for i in range(2**2)]
for i in array:
relation.map_operands("a", i[0])
relation.map_operands("b", i[1])
i.append(relation.evaluate_expression("a | !b ~ b"))
draw_table(array, "a", "b", "a OR NOT(b) IMPLIES b")-----------------------------
a | b | a OR NOT(b) IMPLIES b
-----------------------------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 0
1 | 1 | 1
-----------------------------
Relation Builder (BONUS)¶
Suppose we have a list of relations like, someone is someones parent or children and we want to build relation chains like grandparent, grandchild, cousin etc. We can do that with a simple class.
class People:
def __init__(self, name, parent=None, children=None):
self.name = name
self.parent = parent
self.children = [children]
class Relation:
def __init__(self):
self.map = {}
def add(self, person):
self.map[person.name] = person
def parent(self, parent, child):
self.map[child].parent = parent
if self.map[parent].children == [None]:
self.map[parent].children = [child]
else:
self.map[parent].children.append(child)
def find_grandparent(self, name):
parent = self.map[name].parent
if parent is None:
return None
grandparent = self.map[parent].parent
return grandparentNow, let’s test it out...
relation = Relation()
john = People("John")
mary = People("Mary")
joe = People("Joe")
relation.add(john)
relation.add(mary)
relation.add(joe)
relation.parent("John", "Mary")
relation.parent("Mary", "Joe")
print("Joe's grandparent is:", relation.find_grandparent("Joe"))Joe's grandparent is: John