feat(Algorithms): comparison sort lower bound via decision-tree induction - #770
feat(Algorithms): comparison sort lower bound via decision-tree induction#770SamuelSchlesinger wants to merge 2 commits into
Conversation
…tion A comparison program is a free monad over a single comparison query, i.e. a binary decision tree. card_image_run_le_two_pow_of_cost_le bounds its distinct results over a finite comparator family by 2 ^ t whenever every comparator in the family costs at most t comparisons, by structural induction: the family splits at the root comparison. A program that sorts under every hidden permutation order on Fin n therefore takes at least log2(n!) comparisons in the worst case; (n/2) * log2(n/2) follows as a corollary.
Address the suggestion that data should not be constructed by pattern matching on FreeM: run and cost are now the two projections of the canonical interpretation (FreeM.liftM) of a program into TimeM, so the monad-morphism lemmas run_bind and cost_bind follow from liftM_bind rather than bespoke inductions. The unfolding lemmas remain definitional, and the lower bound proofs are unchanged except that grind now closes their mechanical leaf goals through the grind-annotated unfolding lemmas. Drop two unused imports.
| def run (P : FreeM (SortOps α) β) (le : α → α → Bool) : β := | ||
| (P.liftM (sortHandler le)).ret |
There was a problem hiding this comment.
I don't think Cslib.Algorithms.run should be about SortOps; this needs a better name.
There was a problem hiding this comment.
Alternatively, just drop the run altogether and inline (P.liftM (sortHandler le)).ret below (note that ret has notation)
There was a problem hiding this comment.
Addressed in the PR on top of 685 in Shreyas' repo.
|
Large parts of this resemble #685 verbatim. It would be nice to have a diff. |
| inductive SortOps.{u} (α : Type u) : Type → Type _ where | ||
| /-- `cmpLE x y` is intended to return `true` if `x ≤ y` and `false` otherwise. | ||
| The specific order relation depends on the comparator the program is run against. -/ | ||
| | cmpLE (x : α) (y : α) : SortOps α Bool |
There was a problem hiding this comment.
| @[simp, grind =] | ||
| lemma run_pure (b : β) (le : α → α → Bool) : run (pure b) le = b := rfl | ||
|
|
||
| @[simp, grind =] | ||
| lemma run_lift (x y : α) (le : α → α → Bool) : | ||
| run (FreeM.lift (SortOps.cmpLE x y)) le = le x y := rfl | ||
|
|
||
| @[simp, grind =] | ||
| lemma run_lift_bind (x y : α) (cont : Bool → FreeM (SortOps α) β) (le : α → α → Bool) : | ||
| run ((FreeM.lift (SortOps.cmpLE x y)).bind cont) le = run (cont (le x y)) le := rfl | ||
|
|
||
| @[simp, grind =] | ||
| lemma run_bind (P : FreeM (SortOps α) β) (f : β → FreeM (SortOps α) γ) | ||
| (le : α → α → Bool) : | ||
| run (P >>= f) le = run (f (run P le)) le := by | ||
| simp [run, FreeM.liftM_bind] | ||
|
|
||
| @[simp, grind =] | ||
| lemma cost_pure (b : β) (le : α → α → Bool) : | ||
| cost (pure b : FreeM (SortOps α) β) le = 0 := rfl | ||
|
|
||
| @[simp, grind =] | ||
| lemma cost_lift (x y : α) (le : α → α → Bool) : | ||
| cost (FreeM.lift (SortOps.cmpLE x y)) le = 1 := rfl | ||
|
|
There was a problem hiding this comment.
Standard FreeM/Prog API also found in cslib#685
There was a problem hiding this comment.
Yes, this is the point of this PR: to change parts of the proof, not the statement nor all of the underlying proof machinery. Happy to make this on top of your work, which I have done here: Shreyas4991#3.
An attempt to prove the comparison sort lower bound of #685 taking, in my view, a more natural proof approach for this model. The original proof analyzes traces of the computation, while this approach takes direct advantage of the inductive structure of the free monad and essentially does induction on the program. At a high level, #685 formalizes the encoding argument whereas we formalize the decision tree argument. The arithmetic estimate of log_2 n! is adapted from #685.
Claude Fable 5 authored this code under my direction and review.