forked from ImperialCollegeLondon/M40001_lean
-
Notifications
You must be signed in to change notification settings - Fork 0
/
questions.lean
591 lines (469 loc) · 14 KB
/
questions.lean
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
/-
Copyright (c) 2020 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/
import tactic
/-!
# Logic
A Lean companion to the "Logic" part of the intro module.
We develop the basic theory of the five symbols
→, ¬, ∧, ↔, ∨
# Background
It is hard to ask you difficult questions
about the basic theory of these logical operators,
because every question can be proved by "check all the cases".
However, there is this cool theorem, that says that if
a theorem in the basic theory of logical propositions can be proved
by "check all the cases", then it can be proved in the Lean theorem
prover using only the eight constructive tactics `intro`, `apply`,
`assumption`, `exfalso`, `split`, `cases`, `have`, `left` and `right`,
as well as one extra rule called the Law of the Excluded Middle,
which in Lean is the tactic `by_cases`. Note that the tactic `finish`
is a general "check all the cases" tactic, and it uses `by_cases`.
## Reference
* The first half of section 1 of the M40001/40009 course notes.
-/
namespace xena
variables (P Q R : Prop)
/-
## Level 1 : implies
In Lean, `P → Q` is the notation for `P ⇒ Q` .
Let's start by learning how to control implications. We will
learn the three tactics `intro`, `apply` and `exact`.
-/
/-- Every proposition implies itself. -/
def id : P → P :=
begin
/-
Click here!
See that
`⊢ P → P`
on the top right? That funny symbol `⊢ X` means "you have to prove `X`".
So we have to prove that `P` implies `P`.
How do we prove that `X` implies `Y`? We assume `X`, and try and deduce `Y`.
-/
-- assume P is true. Call this hypothesis hP.
intro hP,
-- goal now `⊢ P` and we also have hypothesis `hP: P`
-- So we know that P is true, by hypothesis hP.
exact hP,
end
-- implication isn't associative!
-- Try it when P, Q, R are all false.
-- `false → (false → false)` is `true`,
-- and
-- `(false → false) → false` is `false`.
-- in Lean, `P → Q → R` is _defined_ to be `P → (Q → R)`
-- Here's a proof of what I just said.
example : (P → Q → R) ↔ (P → (Q → R)) :=
begin
-- ⊢ P → Q → R ↔ P → Q → R
refl -- that closes goals of the form X = X and X ↔ X.
end
-- Another way to see it is just to uncomment out the line below:
-- #check P → (Q → R) -- output is `P → Q → R : Prop`
example : P → Q → P :=
begin
-- remember that by definition the goal is P → (Q → P),
-- so it's P implies something, so let's assume
-- that P is true and call this hypothesis hP.
intro hP,
-- Now we have to prove that Q implies P, so let's
-- assume that Q is true, and let's call this hypothesis hQ
intro hQ,
-- We now have to prove that P is true.
-- But this is exactly our hypothesis hP.
exact hP,
end
/-- If we know `P`, and we also know `P → Q`, we can deduce `Q`. -/
lemma modus_ponens : P → (P → Q) → Q :=
begin
-- remember this means "P implies that ((P implies Q) implies Q)"
-- so let's assume P is true
intro hP,
-- and let's assume hypothesis hPQ, that P implies Q
intro hPQ,
-- now `hPQ` says `P → Q` and we're trying to prove `Q`!
-- So by applying the hypothesis `hPQ`, we can reduce
-- this puzzle to proving `P`.
apply hPQ,
-- Now we have to prove `P`. But this is just an assumption
exact hP, -- or `assumption`
end
-- See if you can do this one yourself. Replace the `sorry` with a proof.
lemma transitivity : (P → Q) → (Q → R) → (P → R) :=
begin
sorry,
end
-- Of course you can always cheat with the `finish` tactic
example : (P → Q) → (Q → R) → (P → R) :=
begin
finish,
end
-- finish just checks all the cases. It's slower than a constructive proof.
-- constructivists regard it as cheating.
-- This one is a "relative modus ponens" -- in the
-- presence of P, if Q -> R and Q then R.
-- Something fun happens in this one. I'll start you off.
example : (P → Q → R) → (P → Q) → (P → R) :=
begin
-- Let `hPQR` be the hypothesis that `P → Q → R`.
intro hPQR,
-- We now need to prove that `(P → Q)` implies something.
-- So let `hPQ` be hypothesis that `P → Q`
intro hPQ,
-- We now need to prove that `P` implies something, so
-- let `hP` be the hypothesis that `P` is true.
intro hP,
-- We now have to prove `R`.
-- We know the hypothesis `hPQR : P → (Q → R)`.
-- Can we apply it?
apply hPQR,
-- exercise: what just happened?
sorry, sorry
end
/-
### Level 2 : not
`not P`, with notation `¬ P`, is defined to mean `P → false` in Lean,
i.e., the proposition that P implies false. Note that `true → false` is `false`,
and `false → false` is `true`, so `P → false` is indeed equivalent
to `¬ P`. But we need to remember the fact that in Lean, `¬ P` was
*defined* to mean `P → false` and not in any other way.
We develop a basic interface for `¬`.
-/
theorem not_not_intro : P → ¬ (¬ P) :=
begin
-- we have to prove that P implies (not (not P)),
-- so let's assume P is true, and let's call this assumption hP
intro hP,
-- now we have to prove `not (not P)`, a.k.a. `¬ (¬ P)`, and
-- by definition this means we have to prove `(¬ P) → false`
-- In fact we can `change` our goal to this
change ¬ P → false,
-- The `change` tactic will make changes to the goal, as long
-- as they are true *by definition*.
-- So let's let hnP be the hypothesis that `¬ P` is true.
intro hnP,
-- and now we have to prove `false`!
-- Sometimes this can be difficult, but it's OK if you have
-- *contradictory hypotheses*, because with contradictory
-- assumptions you can prove false conclusions, and once you've
-- proved one false thing you've proved all false things because
-- you've made mathematics collapse.
-- How are we going to use hypothesis `hnP : ¬ P`?
-- Well, what does it _mean_? It means `P → false`,
-- We could `change` `hnP` to remind us of this:
change P → false at hnP,
-- Now our _goal_ is false, so why don't we apply
-- hypothesis hnP, which will reduce our problem
-- to proving `P`.
apply hnP,
-- now our goal is `P`, and this is an assumption!
exact hP
end
-- What do you think of this proof?
theorem not_not_intro'' : P → ¬ (¬ P) :=
begin
apply modus_ponens,
-- Go back and look at modus ponens. Can you see how this proof worked?
end
-- If you're into lambda calculus or functional programming,
-- here's a functional proof
theorem not_not_intro' : P → ¬ (¬ P) :=
λ hP hnP, hnP hP
-- This one is straightforward -- give it a go:
theorem contra1 : (P → Q) → (¬ Q → ¬ P) :=
begin
sorry
end
-- This way is impossible using constructive logic -- you have to use
-- a classical tactic like `finish` or check manually on cases.
theorem contra2 : (¬ Q → ¬ P) → (P → Q) :=
begin
intro h,
intro hP,
-- stuck
finish,
end
/-!
### Level 3 : and
The hypothesis `hPaQ : P ∧ Q` in Lean, is equivalent to
hypotheses `hP : P` and `hQ : Q`.
If you have `hPaQ` as a hypothesis, and you want to get to
`hP` and `hQ`, you can use the `cases` tactic.
If you have `⊢ P ∧ Q` as a goal, and want to turn the goal
into two goals `⊢ P` and `⊢ Q`, then use the `split` tactic.
Note that after `split` it's good etiquette to use indentation
or brackets, because you have two goals.
Example:
example (hP : P) (hQ : Q) : P ∧ Q :=
begin
split,
exact hP, -- we had two goals here
exact hQ -- we are back to one goal
end
or
example (hP : P) (hQ : Q) : P ∧ Q :=
begin
split,
{ exact hP },
{ exact hQ }
end
-/
theorem and.elim_left : P ∧ Q → P :=
begin
intro hPaQ,
-- You can use the `cases` tactic on an `and` hypothesis
cases hPaQ with hP hQ,
exact hP,
end
-- try this one
theorem and.elim_right : P ∧ Q → Q :=
begin
sorry
end
-- functional proof
theorem and.elim_right' : P ∧ Q → Q := λ hPaQ, hPaQ.2
-- Can you construct the full eliminator for `and`?
theorem and.elim : P ∧ Q → (P → Q → R) → R :=
begin
sorry
end
-- Here's how to solve `and` goals.
theorem and.intro : P → Q → P ∧ Q :=
begin
intro hP,
intro hQ,
-- use `split` on an and goal; you'll get two goals.
split,
assumption, -- just means "the goal is one of the hypotheses"
assumption,
end
-- there's a two-line proof of this which starts
-- `apply function.swap`, but you don't need to do this
theorem and.rec : (P → Q → R) → P ∧ Q → R :=
begin
sorry
end
theorem and.symm : P ∧ Q → Q ∧ P :=
begin
sorry
end
theorem and.trans : (P ∧ Q) → (Q ∧ R) → (P ∧ R) :=
begin
sorry,
end
/-
Extra credit
Recall that the convention for the implies sign →
is that it is _right associative_, by which
I mean that `P → Q → R` means `P → (Q → R)` by definition.
This does actually simplify! If `P` implies `Q → R`
then this means that `P` and `Q` together, imply `R`,
so `P → Q → R` is logically equivalent to `(P ∧ Q) → R`.
We proved that `P → Q → R` implied `(P ∧ Q) → R`; this was `and.rec`.
Let's go the other way.
-/
example : ((P ∧ Q) → R) → (P → Q → R) :=
begin
sorry
end
/-!
### Level 4 : iff
The basic theory of `iff`.
In Lean, `P ↔ Q` is *defined to mean* `(P → Q) ∧ (Q → P)`.
It is _not_ defined by a truth table. You can attack a `P ↔ Q` goal
with the `split` tactic, because it is really an `∧` statement.
-/
/-- `P ↔ P` is true for all propositions `P`. -/
def iff.refl : P ↔ P :=
begin
-- By Lean's definition I need to prove (P → P) ∧ (P → P)
split,
-- need to prove P → P
-- We proved that a long time ago and called it `id`.
apply id,
-- need to prove P → P
apply id
end
-- If you get stuck, there is always the "truth table" tactic `finish`
def iff.refl' : P ↔ P :=
begin
finish,
end
-- The refl tactic also works
def iff.refl'' : P ↔ P :=
begin
refl
end
def iff.symm : (P ↔ Q) → (Q ↔ P) :=
begin
-- Try this one using `cases` and `split`.
sorry
end
-- I'll now show you a better way: the `rewrite` tactic.
def iff.symm' : (P ↔ Q) → (Q ↔ P) :=
begin
intro h,
-- `h : P ↔ Q`
-- The `rw h` tactic will change all P's in the goal to Q's.
-- And then it will try `refl`, just for luck
rw h,
-- finished! Goal becamse `Q ↔ Q` and then `refl` finished it.
end
def iff.comm : (P ↔ Q) ↔ (Q ↔ P) :=
begin
sorry,
end
-- without rw or cc this is ugly
def iff.trans : (P ↔ Q) → (Q ↔ R) → (P ↔ R) :=
begin
sorry
end
-- This is a cute question. Can you prove it constructively,
-- using only `intro`, `cases`, `have`, `apply`, and `assumption`?
def iff.boss : ¬ (P ↔ ¬ P) :=
begin
sorry
end
-- Now we have iff we can go back to and.
/-! ### iff epilogue: ↔ and ∧ -/
theorem and.comm : P ∧ Q ↔ Q ∧ P :=
begin
sorry
end
-- ∧ is "right associative" in Lean, which means
-- that `P ∧ Q ∧ R` is _defined to mean_ `P ∧ (Q ∧ R)`.
-- Associativity can hence be written like this:
theorem and_assoc : ((P ∧ Q) ∧ R) ↔ (P ∧ Q ∧ R) :=
begin
sorry,
end
/-!
## Level 5 (final level) : Or
`P ∨ Q` is true when at least one of `P` and `Q` are true.
Here is how to work with `∨` in Lean.
If you have a _hypothesis_ `hPoQ : P ∨ Q` then you
can break into the two cases `hP : P` and `hQ : Q` using
`cases hPoQ with hP hQ`
If you have a _goal_ of the form `⊢ P ∨ Q` then you
need to decide whether you're going to prove `P` or `Q`.
If you want to prove `P` then use the `left` tactic,
and if you want to prove `Q` then use the `right` tactic.
Don't get lost! You can't go back.
-/
-- recall that P, Q, R are Propositions. We'll need S for this one.
variable (S : Prop)
-- use the `left` tactic to reduce from `⊢ P ∨ Q` to `⊢ P`
theorem or.intro_left : P → P ∨ Q :=
begin
intro hP,
-- ⊢ P ∨ Q
left,
-- ⊢ P
exact hP
end
-- use the `right` tactic to reduce from `⊢ P ∨ Q`
theorem or.intro_right : Q → P ∨ Q :=
begin
sorry,
end
theorem or.elim : P ∨ Q → (P → R) → (Q → R) → R :=
begin
intro hPoQ,
intros hpq hqr,
-- use the `cases h` tactic if `h : X ∨ Y`
cases hPoQ with hP hQ,
sorry,
sorry
end
theorem or.symm : P ∨ Q → Q ∨ P :=
begin
sorry
end
theorem or.comm : P ∨ Q ↔ Q ∨ P :=
begin
sorry
end
theorem or.assoc : (P ∨ Q) ∨ R ↔ P ∨ Q ∨ R :=
begin
sorry,
end
theorem or.cases_on : P ∨ Q → (P → R) → (Q → R) → R :=
begin
sorry,
end
theorem or.imp : (P → R) → (Q → S) → P ∨ Q → R ∨ S :=
begin
sorry,
end
theorem or.imp_left : (P → Q) → P ∨ R → Q ∨ R :=
begin
sorry,
end
theorem or.imp_right : (P → Q) → R ∨ P → R ∨ Q :=
begin
sorry,
end
theorem or.left_comm : P ∨ Q ∨ R ↔ Q ∨ P ∨ R :=
begin
sorry,
end
theorem or.rec : (P → R) → (Q → R) → P ∨ Q → R :=
begin
sorry
end
theorem or.resolve_left : P ∨ Q → ¬P → Q :=
begin
sorry,
end
theorem or_congr : (P ↔ R) → (Q ↔ S) → (P ∨ Q ↔ R ∨ S) :=
begin
sorry,
end
/-!
# Appendix: `exfalso` and classical logic
-/
-- useful lemma about false
theorem false.elim' : false → P :=
begin
-- Let's assume that a false proposition is true. Let's
-- call this assumption h.
intro h,
-- We now have to prove P.
-- The `exfalso` tactic changes any goal to `false`.
exfalso,
-- Now our goal is an assumption! It's exactly `h`.
exact h,
end
-- Is that confusing? What about this proof?
theorem false.elim'' : false → P :=
begin
-- Let's assume that a false proposition is true. Let's
-- call this assumption h.
intro h,
-- Now let's deal with all the cases.
cases h,
-- There are no cases.
end
-- This next one cannot be proved using the tactics we know
-- which are constructive. This one needs the assumption
-- that every statement is true or false.
-- We give a "by cases" proof explicitly -- `finish` just does the
-- job immediately.
theorem double_negation_elimination : ¬ (¬ P) → P :=
begin
-- `finish` works
classical,
intro hnnP,
by_cases hP : P,
-- hypothesis hP : P
assumption,
-- hypothesis hP : ¬ P
-- `contradiction` works from here
exfalso,
apply hnnP,
exact hP,
end
end xena