Zulip Chat Archive

Stream: maths

Topic: decreasing induction


Sebastien Gouezel (Apr 09 2019 at 19:22):

I can not find the fact that, if a property P n holds and if P (i+1) implies P i for all i, then P m holds whenever m <= n. Is this already in mathlib?

Sebastien Gouezel (Aug 19 2021 at 17:56):

We have currently a decreasing induction principle on nat, in the form

def decreasing_induction {P :   Sort*} (h : k, P (k+1)  P k) {m n : } (mn : m  n)
  (hP : P n) : P m :=
le_rec_on mn (λ k ih hsk, ih $ h k hsk) (λ h, h) hP

(notice the completely unreadable proof :-). This is even able to construct data, as testified by the fact that P takes values in Sort*. Unfortunately, the assumption is too strong: one should only need h for k < n, not for all k. It is easy to prove it with this weaker assumption (just write m = n - i and do a usual induction on i), but it is not obvious to me if this kind of proof is able to construct data. So, I came up with the following proof, which avoids subtraction and is based on the above decreasing_induction, but my proof takes 30 lines instead of 1, so it is obviously wrong. Here it is:

import data.nat.basic

open nat

@[elab_as_eliminator]
def decreasing_induction' {P :   Sort*} {m n : } (h :  k < n, P (k+1)  P k) (mn : m  n)
  (hP : P n) : P m :=
begin
  let Q := λ k, if k  n then P k else P n,
  let H :  k, Q (k+1)  Q k := λ k a, if hk : k < n then
  begin
    have Qk' : Q (k+1) = P (k+1),
    { simp only [not_le, ite_eq_left_iff],
      assume hk',
      exact (lt_irrefl _ ((succ_le_iff.2 hk).trans_lt hk')).elim },
    have Qk : Q k = P k,
    { simp only [not_le, ite_eq_left_iff],
      assume hk',
      exact (lt_irrefl _ (hk.trans hk')).elim },
    exact cast Qk.symm (h k hk (cast Qk' a))
  end else begin
    simp only [not_lt] at hk,
    have Qk' : Q (k+1) = P n,
    { simp only [ite_eq_right_iff],
      assume hk',
      exact (lt_irrefl _ ((succ_le_iff.1 hk').trans_le hk)).elim },
    have Qk : Q k = P n,
    { simp only [ite_eq_right_iff],
      assume hk',
      rw le_antisymm hk hk' },
    exact cast Qk.symm ((cast Qk' a))
  end,
  have Qn : Q n = P n, by simp [Q],
  have Qm : Q m = P m,
  { simp only [not_le, ite_eq_left_iff],
    assume hmn,
    exact (lt_irrefl _ (hmn.trans_le mn)).elim },
  exact cast Qm (decreasing_induction H mn (cast Qn.symm hP)),
end

How would a more proper proof look like?

Floris van Doorn (Aug 19 2021 at 18:36):

Here is the proof in the unreadable style:

import data.nat.basic

open nat

@[elab_as_eliminator]
def decreasing_induction' {P :   Sort*} {m n : } (h :  k < n, P (k+1)  P k)
  (mn : m  n) (hP : P n) : P m :=
le_rec_on mn (λ n ih h hn, ih (λ k hk, h k hk.step) (h n (lt_succ_self n) hn)) (λ _, id) h hP

Floris van Doorn (Aug 19 2021 at 18:40):

or, the same but more readable:

@[elab_as_eliminator]
def decreasing_induction' {P :   Sort*} {m n : } (h :  k < n, P (k+1)  P k)
  (mn : m  n) (hP : P n) : P m :=
begin
  -- induction mn using nat.le_rec_on generalizing h hP -- this doesn't work unfortunately
  refine le_rec_on mn _ _ h hP; clear h hP mn n,
  { intros n ih h hP,
    apply ih,
    { exact λ k hk, h k hk.step },
    { exact h n (lt_succ_self n) hP } },
  { intros h hP, exact hP }
end

Floris van Doorn (Aug 19 2021 at 18:53):

You could even weaken the assumption more:

/-- Recursion starting at a non-zero number: given a map `C k → C (k+1)` for each `k ≥ n`,
there is a map from `C n` to each `C m`, `n ≤ m`. -/
@[elab_as_eliminator]
def le_rec_on' {C :   Sort*} {n : } :
  Π {m : }, n  m  (Π k⦄, n  k  C k  C (k+1))  C n  C m
| 0     H next x := eq.rec_on (nat.eq_zero_of_le_zero H) x
| (m+1) H next x := or.by_cases (of_le_succ H) (λ h : n  m, next h $ le_rec_on' h next x)
  (λ h : n = m + 1, eq.rec_on h x)

@[elab_as_eliminator]
def decreasing_induction' {P :   Sort*} {m n : } (h :  k < n, m  k  P (k+1)  P k)
  (mn : m  n) (hP : P n) : P m :=
begin
  -- induction mn using nat.le_rec_on' generalizing h hP -- this doesn't work unfortunately
  refine le_rec_on' mn _ _ h hP; clear h hP mn n,
  { intros n mn ih h hP,
    apply ih,
    { exact λ k hk, h k hk.step },
    { exact h n (lt_succ_self n) mn hP } },
  { intros h hP, exact hP }
end

Scott Morrison (Aug 20 2021 at 00:39):

Thanks for doing this, @Sebastien Gouezel and @Floris van Doorn: I'd just been sad yesterday that this was missing.


Last updated: Dec 20 2023 at 11:08 UTC