-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathformal_logic.py
More file actions
140 lines (113 loc) · 5.12 KB
/
formal_logic.py
File metadata and controls
140 lines (113 loc) · 5.12 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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"""Formal logic — propositions, formulas, laws of thought, inference.
GUTE relevance: Ψ (observer as selector — logical assignment chooses one
world from the set of possible worlds; the laws of thought are the
stability of that selection).
Ported from divine-os-old. Kept: LogicalOperator, Proposition, Formula
(evaluate under truth assignment), LawsOfThought (identity, non-contradiction,
excluded middle), InferenceRules (modus ponens, modus tollens). Dropped:
the modal / temporal / first-order fragments — no slice needs them yet.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Any
class LogicalOperator(str, Enum):
NOT = "¬"
AND = "∧"
OR = "∨"
IMPLIES = "->"
IFF = "<->"
@dataclass
class Proposition:
"""An atomic proposition — a symbol with a truth value (from assignment)."""
symbol: str
value: bool | None = None
def __str__(self) -> str:
return self.symbol
def evaluate(self, assignment: dict[str, bool]) -> bool:
if self.symbol in assignment:
return bool(assignment[self.symbol])
raise ValueError(f"No truth value assigned to {self.symbol}")
@dataclass
class Formula:
"""A compound formula built from an operator and operands.
When `operator is None`, the Formula is atomic and operands[0] is a
Proposition.
"""
operator: LogicalOperator | None
operands: list[Any]
def __str__(self) -> str:
if self.operator is None:
return str(self.operands[0])
if self.operator == LogicalOperator.NOT:
return f"¬{self.operands[0]}"
return f"({self.operands[0]} {self.operator.value} {self.operands[1]})"
def evaluate(self, assignment: dict[str, bool]) -> bool:
if self.operator is None:
return bool(self.operands[0].evaluate(assignment))
if self.operator == LogicalOperator.NOT:
return not self.operands[0].evaluate(assignment)
if self.operator == LogicalOperator.AND:
return all(op.evaluate(assignment) for op in self.operands)
if self.operator == LogicalOperator.OR:
return any(op.evaluate(assignment) for op in self.operands)
if self.operator == LogicalOperator.IMPLIES:
p = self.operands[0].evaluate(assignment)
q = self.operands[1].evaluate(assignment)
return (not p) or q
if self.operator == LogicalOperator.IFF:
p = self.operands[0].evaluate(assignment)
q = self.operands[1].evaluate(assignment)
return bool(p == q)
raise ValueError(f"Unknown operator: {self.operator}")
class LawsOfThought:
"""Classical Aristotelian laws — identity, non-contradiction, excluded middle."""
@staticmethod
def law_of_identity(p: Proposition, assignment: dict[str, bool]) -> bool:
"""A → A. Always true."""
formula = Formula(LogicalOperator.IMPLIES, [p, p])
return formula.evaluate(assignment)
@staticmethod
def law_of_non_contradiction(p: Proposition, assignment: dict[str, bool]) -> bool:
"""¬(A ∧ ¬A). Always true."""
not_p = Formula(LogicalOperator.NOT, [p])
contradiction = Formula(LogicalOperator.AND, [p, not_p])
negation = Formula(LogicalOperator.NOT, [contradiction])
return negation.evaluate(assignment)
@staticmethod
def law_of_excluded_middle(p: Proposition, assignment: dict[str, bool]) -> bool:
"""A ∨ ¬A. Always true (in classical logic)."""
not_p = Formula(LogicalOperator.NOT, [p])
formula = Formula(LogicalOperator.OR, [p, not_p])
return formula.evaluate(assignment)
@staticmethod
def verify_all_laws(p: Proposition) -> dict[str, bool]:
"""Run all three laws under both possible assignments of p."""
results: dict[str, bool] = {}
for value in (True, False):
assignment = {p.symbol: value}
results[f"identity_P={value}"] = LawsOfThought.law_of_identity(p, assignment)
results[f"non_contradiction_P={value}"] = LawsOfThought.law_of_non_contradiction(
p, assignment
)
results[f"excluded_middle_P={value}"] = LawsOfThought.law_of_excluded_middle(
p, assignment
)
return results
class InferenceRules:
"""Standard propositional inference rules."""
@staticmethod
def modus_ponens(p: Formula, p_implies_q: Formula, assignment: dict[str, bool]) -> bool:
"""P, P→Q ⊢ Q."""
if p.evaluate(assignment) and p_implies_q.evaluate(assignment):
if p_implies_q.operator == LogicalOperator.IMPLIES:
return bool(p_implies_q.operands[1].evaluate(assignment))
return False
@staticmethod
def modus_tollens(not_q: Formula, p_implies_q: Formula, assignment: dict[str, bool]) -> bool:
"""¬Q, P→Q ⊢ ¬P."""
if not_q.evaluate(assignment) and p_implies_q.evaluate(assignment):
if p_implies_q.operator == LogicalOperator.IMPLIES:
p = p_implies_q.operands[0]
return not p.evaluate(assignment)
return False