-
Notifications
You must be signed in to change notification settings - Fork 0
/
propositional_logic_final.jl
312 lines (242 loc) · 8.34 KB
/
propositional_logic_final.jl
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#### The meta-language
#Negation
(¬)(p::Bool)::Bool = !p
#Disjunction
∨(p::Bool, q::Bool)::Bool = p || q
#Conjunction
∧(p::Bool, q::Bool)::Bool = ¬(¬p ∨ ¬q)
#Implication
⟹(p::Bool, q::Bool)::Bool = ¬p ∨ q
#Biconditional
⟺(p::Bool, q::Bool)::Bool = (p ⟹ q) ∧ (q ⟹ p)
#The data-type for well-formed formulae
WFF = Union{Symbol, Expr}
#The language
𝕃 = (:p, :q, :r)
"""
Generates all well-formed formulae of size `n`.
S𝕃(n) = S𝕃(n-1) ∪ {(¬s), (s ∨ t) | s, t ∈ S𝕃(n-1)}
"""
function S𝕃(n::Int = 0)
n == 0 ?
[𝕃...] :
[
S𝕃(n-1);
[:(¬$s) for s ∈ S𝕃(n-1)];
[:($s∨$t) for s ∈ S𝕃(n-1) for t ∈ S𝕃(n-1)]
]
end
SL = S𝕃(3)
rand(SL)
"""
Rewrites a formula, replacing a given symbol with a boolean value (true / false).
"""
function rewrite!(formula::WFF, s::Symbol, v::Bool)
for (i, e) in enumerate(formula.args)
if e == s
formula.args[i] = v
elseif typeof(e) == Expr
rewrite!(e, s, v)
end
end
end
#A demonstration of the `rewrite!` function
f = :((¬p ∨ (q ∧ ¬r)) ∨ (p ⟹ ¬p))
rewrite!(f, :p, true)
f
"""
Applies a given valuation to a formula `φ`.
"""
function evaluate(φ::WFF, v::Tuple{Vararg{Bool}})::Bool
formula = deepcopy(φ) #copying to preserve the input formula
if typeof(φ) == Symbol
formula = Expr(:call, :(x -> x), formula) #handling symbols
end
for (val, s) ∈ zip(v, 𝕃)
rewrite!(formula, s, val)
end
eval(formula)
end
#A demonstration of the `evaluate` function
ϕ = :((¬r ⟹ (q ∧ p)) ⟺ (((r ∨ ¬p) ∧ (p ⟹ q)) ∨ p))
evaluate(ϕ, (true, false, true))
"""
Checks if a valuation v satisfies a given set of formulae.
"""
evaluate(formulae::Vector{Any}, v::Tuple{Vararg{Bool}})::Bool = reduce(∧, [evaluate(formula, v) for formula ∈ formulae])
"""
Gives the list of all valuations that satisfy the given formula.
"""
function ν(formula::Union{WFF, Vector{Expr}})::Vector{Tuple{Vararg{Bool}}}
#the set of all possible valuations for the symbols in 𝕃
vs = Iterators.product(ntuple(i -> (false, true), length(𝕃))...) |> collect |> vec
[v for v ∈ vs if evaluate(formula, v)]
end
"""
Checks if a proposition `t` is a logical consequence of a given set of propositions `S`.
In other words, this function checks if `S ⊨ t`.
"""
⊩(S::Vector{Any}, t::WFF)::Bool = ν(S) ⊆ ν(t)
#A demonstration of the `⊩` function
[:(p ∧ p), :(p ⟹ r)] ⊩ :(r ∨ r)
"""
Checks if two propositional formulae `s` and `t` are logically equivalent (`s ≡ t`).
"""
≌(s::WFF, t::WFF)::Bool = (ν(s) ⊆ ν(t)) ∧ (ν(t) ⊆ ν(s))
#A demonstration of the `≌` function
:(p ∨ ¬(p ⟹ q)) ≌ :(p ∧ p)
#### Hilbert-style deductive calculus
#The Logical Axioms
LA₁(s::WFF, t::WFF)::WFF = :( $s ⟹ ($t ⟹ $s) )
LA₂(s::WFF, t::WFF, u::WFF)::WFF = :( ($s ⟹ ($t ⟹ $u)) ⟹ (($s ⟹ $t) ⟹ ($s ⟹ $u)) )
LA₃(s::WFF)::WFF = :( ¬¬$s ⟹ $s )
LA₄(s::WFF, t::WFF)::WFF = :( (¬$s ⟹ ¬$t) ⟹ ($t ⟹ $s) )
#Modus Ponens
MP(s::WFF, t::WFF)::WFF = s.args[begin] == :⟹ && (s.args[begin+1] == t) ? s.args[end] : :missing
#Non-Logical Axioms
NLA(S , n::Int)::WFF = S[n]
"""
A data structure defining sequents.
It contains the set of non-logical axioms `S`,
a propositional formula `t`,
a rule (logical axiom, rule of inference, or non-logical axiom) used to derive the proposition,
a list of arguments passed to the rule function.
`S ⊢ t`
"""
struct Sequent
S::Vector{WFF}
t::WFF
rule::Function
args::Vector{Any}
#checks if the sequent rule gives the required formula
Sequent(S, t, rule, args) = rule != MP && rule(args...) != t ?
error("Inconsistent sequent") :
new(S, t, rule, args)
end
#The data-type for formal proofs
FormalProof = Vector{Sequent} #a FormalProof here is implemented as a list of Sequents
"""
A function that takes in a FormalProof (a list of sequents),
and verifies if the steps are correct.
If the formal proof is correct, the last propositional formula is returned.
"""
function verify(FP::FormalProof)
for (i, Seq) ∈ enumerate(FP)
if Seq.rule == MP
#The indices here are relative positions
m, n = Seq.args[begin]
MP(FP[i - m].t, FP[i - n].t) == Seq.t ?
println("Step $i involving MP is correct.") :
error("Error in step $i in the formal proof.")
end
end
println("\n The formal proof is correct!")
return FP[end].t
end
#A demonstration of formal proof verification for the propositional formula `p ⟹ r`.
let S = [:(p ⟹ q), :(q ⟹ r)]
proof::FormalProof = [
Sequent(S, :(q ⟹ r), NLA, [S, 2]),
Sequent(S, :(p ⟹ q), NLA, [S, 1]),
Sequent(S, :((q ⟹ r) ⟹ (p ⟹ (q ⟹ r))), LA₁, [:(q ⟹ r), :(p)]),
Sequent(S, :((p ⟹ (q ⟹ r))), MP, [(1, 3)]),
Sequent(S, :( (p ⟹ (q ⟹ r)) ⟹ ((p ⟹ q) ⟹ (p ⟹ r)) ), LA₂, [:p, :q, :r]),
Sequent(S, :((p ⟹ q) ⟹ (p ⟹ r)), MP, [(1, 2)]),
Sequent(S, :(p ⟹ r), MP, [(1, 5)])
]
verify(proof)
end
#Verification of the proof of :((s ⟹ ¬¬s))
let S = Expr[]
proof::FormalProof = [
Sequent(S, :(¬(¬(¬s)) ⟹ ¬s), LA₃, [:(¬s)]),
Sequent(S, :( (¬(¬(¬s)) ⟹ ¬s) ⟹ (s ⟹ ¬(¬s))), LA₄, [:(¬(¬s)), :(s)]),
Sequent(S, :(s ⟹ ¬(¬s)), MP, [(1, 2)])
]
verify(proof)
end
"""
Recurses through the expression `expr`, searching for an occurrence of the expression `pf` on the right of an implication sign.
The output is a vector of terms that were on the left side of an implication sign.
"""
function nested(expr::WFF, pf::WFF)
if expr == pf
return [pf]
end
if typeof(expr) != Symbol && expr.args[begin] == :⟹
a, b = expr.args[end-1:end]
return [a; nested(b, pf)]
end
return expr
end
"""
Returns a set of tautologies obtained by plugging the elements of the input set `S` into to the logical axioms.
A dictionary is used to store information regarding which logical axiom and inputs were used to produce a particular expression.
"""
function τ(S::Vector{Any})
T, D = Expr[], Dict{Expr, Tuple{Function, Vector{Any}}}()
for a ∈ S
push!(T, LA₃(a)); push!(D, LA₃(a)=>(LA₃, [a]))
for b ∈ S
push!(T, LA₁(a, b)); push!(D, LA₁(a, b)=>(LA₁, [a, b]))
push!(T, LA₄(a, b)); push!(D, LA₄(a, b)=>(LA₄, [a, b]))
for c ∈ S
push!(T, LA₂(a, b, c)); push!(D, LA₂(a, b, c)=>(LA₂, [a, b, c]))
end
end
end
(T, D)
end
"""
Produces new propositional formulae from the given set `S` by recursively joining existing propositions with `¬` and `⟹`.
"""
function R𝕃(S, n::Int)
n == 0 ?
S :
[
R𝕃(S, n-1);
[:(¬$s) for s ∈ R𝕃(S, n-1)];
[:($s⟹$t) for s ∈ R𝕃(S, n-1) for t ∈ R𝕃(S, n-1)]
] |> Set |> collect
end
𝕃 = (:s,)
"""
Returns a formal proof (list of sequents) of a propositional formula `prop`, using tautologies specified in the list `T`.
The output is an empty list is no proof can be produced using the tautologies in `T`.
"""
function proof_of(prop::WFF, T, D; avoid = [])
formalproof = Sequent[]
function explore()
ℒ = [(nested(t, prop), t) for t ∈ T]
filter!(V -> V[1][end] == prop, ℒ)
for l ∈ ℒ
push!(formalproof, Sequent(Expr[], l[end], D[l[end]]...))
seqprove(l...)
length(formalproof) == 0 ? continue : break
end
return formalproof
end
function seqprove(props, expr)
if length(props) == 1
return
end
if (props[begin] ∈ avoid) || length(ν(props[begin])) < 2^length(𝕃)
formalproof = Sequent[]
return
end
pr = proof_of(props[begin], T, D; avoid = [[props[end]]; avoid])
if length(pr) == 0
formalproof = Sequent[]
return
end
append!(formalproof, pr)
push!(formalproof, Sequent(Expr[], expr.args[end], MP, [(1, 1+length(pr))]))
seqprove(props[begin+1:end], expr.args[end])
end
explore()
end
proof_of(prop::WFF, (S, n)) = proof_of(prop, τ(R𝕃(S, n))...)
#Examples of proofs
proof_of(:(s ⟹ ¬¬s), ([:s], 2))
proof_of(:(s ⟹ s), ([:s], 1))
proof_of(:((s ⟹ ¬s) ⟹ ¬s), ([:s], 1)) #this has no proofs involving the given tautologies