Zulip Chat Archive
Stream: new members
Topic: Simple Termination Argument for Quicksort?
Agnishom Chattopadhyay (Oct 18 2023 at 14:27):
I am trying to learn Lean4's functional programming capabilities by writing some simple programs.
How do I complete this definition for Quicksort?
-- write a simple quicksort function
def quicksort : List Nat → List Nat
| [] => []
| x :: xs =>
quicksort (xs.filter (· < x))
++ [x] ++
quicksort (xs.filter (· ≥ x))
termination_by
quicksort xs => xs.length
decreasing_by
sorry
#eval quicksort [3, 4, 2, 1]
-- prints [1, 2, 3, 4]
Martin Dvořák (Oct 18 2023 at 14:44):
import Mathlib.Data.List.Basic
def quicksort : List Nat → List Nat
| [] => []
| x :: xs =>
quicksort (xs.filter (· < x))
++ [x] ++
quicksort (xs.filter (· ≥ x))
termination_by
quicksort xs => xs.length
decreasing_by
simp_wf
rw [Nat.lt_succ]
apply List.length_filter_le
Agnishom Chattopadhyay (Oct 18 2023 at 14:48):
Thanks! I have some follow up questions.
- When I put my cursor after the
decreasing_by
, I do not see any goals. Why is that? - After I write
simp_wf
, I see two goals. Which goal do the subsequent tactics apply to? - Why doesn't writing
apply?
orexact?
aftersimp_wf
give me any helpful suggestions?
Martin Dvořák (Oct 18 2023 at 14:49):
I had similar questions recently:
https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Unfocus.20goal.20after.60decreasing_by.60
Agnishom Chattopadhyay (Oct 18 2023 at 15:06):
Oh, interesting. Thanks!
Now, I have
def quicksort : List Nat → List Nat
| [] => []
| x :: xs =>
(
have : List.length (xs.filter (· < x)) < List.length (x :: xs) := by {
simp [List.length]
apply Nat.lt_succ_of_le
apply List.length_filter_le
}
quicksort (xs.filter (· < x))
)
++ [x] ++
(
have : List.length (xs.filter (· ≥ x)) < List.length (x :: xs) := by {
simp [List.length]
apply Nat.lt_succ_of_le
apply List.length_filter_le
}
quicksort (xs.filter (· ≥ x))
)
termination_by
quicksort xs => xs.length
decreasing_by
simp_wf
simp_all
But I have no idea what is happening here.
- Why can't I see the goals that are created by
decreasing_by
? - Does
decreasing_by
have to be followed bysimp_wf
? Issimp_wf
a tactic? - How do I select the individual goals after
simp_wf
? - What is the meaning of the term
(have : ... := ...; x)
? Is it the same thing asx
? What type does it have?
Alex J. Best (Oct 18 2023 at 18:42):
simp_wf
is a tactic, you should be able to right click it and jump to its definition to see what its implementation is (its not that complicated iirc, just simp with some extra lemmas thrown in that make it useful for well foundedness proofs).
Alex J. Best (Oct 18 2023 at 18:42):
You don't have to use simp_wf
after decreasing by, but probably 90% of the time it does something useful.
Alex J. Best (Oct 18 2023 at 18:45):
#check (have a : True := trivial; 1)
shows you what the have statement elaborates to. It should be definitionally equal to just x
in your example but it is not literally x
.
Alex J. Best (Oct 18 2023 at 18:46):
If I put my cursor on the simp_wf
in your big example I can see two goals, so I don't understand your first question
Martin Dvořák (Oct 18 2023 at 18:52):
Alex J. Best said:
#check (have a : True := trivial; 1)
shows you what the have statement elaborates to. It should be definitionally equal to justx
in your example but it is not literallyx
.
I think the problem is, if instead of have
you want to prove termination after the decreasing_by
keyword, both goals are focused. And I don't know how to focus only one of them. As a result, many tools don't work for me the way they should.
Alex J. Best (Oct 18 2023 at 19:12):
Yeah as you may have noticed that thing isn't really a normal goal state with a by block, whatever tactic you add there is used to fill all decreasing proofs and the state for all needed proofs is displayed in that one spot.
So you probably have to use things like first | tac1 | tac2
to be more flexible if the proofs are complicated
Alex J. Best (Oct 18 2023 at 19:12):
Thats why most of the time its better to put the proofs as have
statements in the term
Agnishom Chattopadhyay (Oct 19 2023 at 14:15):
Alex J. Best said:
If I put my cursor on the
simp_wf
in your big example I can see two goals, so I don't understand your first question
Yes, but the goals are only seen _after_ running simp_wf
. How do I know what the goal was _before_ running simp_wf
?
Agnishom Chattopadhyay (Oct 19 2023 at 14:18):
Alex J. Best said:
#check (have a : True := trivial; 1)
shows you what the have statement elaborates to. It should be definitionally equal to justx
in your example but it is not literallyx
.
That's interesting. I saw that have a : x = y; b
is syntactic sugar for (fun a => b) y
. I am not really sure how this lets you feed in y into the proofs later though
Agnishom Chattopadhyay (Oct 19 2023 at 14:19):
Martin Dvořák said:
I think the problem is, if instead of
have
you want to prove termination after thedecreasing_by
keyword, both goals are focused. And I don't know how to focus only one of them. As a result, many tools don't work for me the way they should.
Yes, exactly. I do not really understand how to focus on individual goals in this situation. With Coq, I would have used -
or {
but I do not know what is the analogue for that in Lean
Alex J. Best (Oct 19 2023 at 15:50):
For normal proofs you can use .
or \.
to focus a goal, but a decreasing_by
block is unfortunately not really a normal proof. I'd recommend proving as much as possible inline in the description of the algorithm, using List.length (xs.filter (· ≥ x)) < List.length (x :: xs)
etc where you can use focussing like normal
Alex J. Best (Oct 19 2023 at 15:51):
Agnishom Chattopadhyay said:
Alex J. Best said:
If I put my cursor on the
simp_wf
in your big example I can see two goals, so I don't understand your first questionYes, but the goals are only seen _after_ running
simp_wf
. How do I know what the goal was _before_ runningsimp_wf
?
If I put my cursor before the s
in simp_nf
I see the goal before, if I put it after I see the goal after, there is even red/green highlighting to show what changed, in this case basically the whole goal
Agnishom Chattopadhyay (Oct 20 2023 at 16:33):
Thanks, the goal before the simp_wf
now seems to be visible
Agnishom Chattopadhyay (Oct 20 2023 at 16:35):
How do I complete this argument?
def gcd : Nat → Nat → Nat
| 0, y => y
| x, 0 => x
| x, y =>
if x > y
then (have : x - y < x := by {
sorry -- can't see the hypotheses x > y and x ≠ 0
} ;
gcd (x - y) y)
else (have : y - x < y := sorry; gcd x (y - x))
termination_by
gcd x y => x + y
decreasing_by
simp_wf
simp_all
The hypotheses x > y
and x <> 0
seem to be missing from the context
Alex J. Best (Oct 20 2023 at 16:41):
You can do if h : x > y
. In this case x > y
is enough to prove x \ne 0
also so that shouldn't be needed
Agnishom Chattopadhyay (Oct 20 2023 at 17:31):
Seems like linarith
is not able to resolve this goal
| x, y =>
if h : x > y
then (have : x - y < x := by {
linarith
} ;
gcd (x - y) y)
else (have : y - x < y := sorry; gcd x (y - x))
Alex J. Best (Oct 20 2023 at 17:34):
If its an inequality about nats, then you probably need to be more careful with subtraction.
try zify [h]
as a first step maybe
Agnishom Chattopadhyay (Oct 20 2023 at 17:46):
I think we need the hypothesis y <> 0
. If y = 0
, then we would have x - y = x
Alex J. Best (Oct 20 2023 at 17:54):
Probably the simplest solution is to write it as | (x + 1), (y+ 1) => ...
then
Last updated: Dec 20 2023 at 11:08 UTC