Zulip Chat Archive

Stream: new members

Topic: Contributing a strong induction for lists


Will Crichton (Jun 05 2024 at 10:44):

I implemented a strong induction principle for lists that works over all suffixes. The signature is:

def List.IsStrictSuffix {α} (l1 l2 : List α) :=  t  [], t ++ l1 = l2

def List.suffix_induction {α} {p : List α  Prop}
  (l : List α)
  (nil : p nil)
  (cons :  head tail, ( l', l'.IsStrictSuffix (head :: tail)  p l')  p (head :: tail))
  : p l

Full source link.

Would it be useful to submit this as a contribution to Mathlib? I was hoping something like this was in Mathlib, but there wasn't, so I implemented it myself.

Joachim Breitner (Jun 05 2024 at 13:13):

Is this principle more useful than using strong induction on list lengths? It seems that if you can prove IsStrictSuffix you can prove that one list is smaller than the other one easily.

Joachim Breitner (Jun 05 2024 at 13:14):

Also, it may not be needed to have a separate nil and cons case, and a single case might be nicer.

Will Crichton (Jun 05 2024 at 13:15):

I'm actually not sure how to prove the induction principle for list lengths. That was my first intuition, but you end up needing to show that l1.length < l2.length -> sizeOf l1 < sizeOf l2 and that is not true. Maybe I did something wrong in the proof.

Will Crichton (Jun 05 2024 at 13:15):

But yes more generally, I think Mathlib should have some strong induction principle for lists. This is just the one I could figure out :-)

Joachim Breitner (Jun 05 2024 at 13:51):

Try termination_by l.length before your decreasing_by

Joachim Breitner (Jun 05 2024 at 14:01):

Also, there isn’t really anything special about List.length here, so a single induction principle for any α → Nat should suffice:

def Nat.strongIndutionMeasureOn {α} {motive : α  Sort _} (f : α  Nat)
   (x : α) (step : (x : α)  ((y : α)  f y < f x  motive y)  motive x) : motive x :=
  step x (fun y _ => Nat.strongIndutionMeasureOn f y step)
termination_by f x

example (x : List Nat) : x = x := by
  induction x using Nat.strongIndutionMeasureOn (f := List.length)
  case step x ih =>
    -- 1 goal
    -- x: List Nat
    -- ih: ∀ (y : List Nat), y.length < x.length → y = y
    -- ⊢ x = x
    sorry

llllvvuu (Jun 05 2024 at 22:35):

Joachim Breitner said:

Is this principle more useful than using strong induction on list lengths?

Maybe if there is a use case where the statement is indeed true only for suffixes and not arbitrary lists, then one would need to create this hypothesis, e.g.:

import Batteries.Data.List.Lemmas
import Mathlib.Tactic.Cases

example (x : List Nat) : x = x := by
  generalize hy : x = y
  replace hy : y <:+ x := hy  x.suffix_refl

  -- induction y using Nat.strongInductionMeasureOn List.length with | _ y ih => ?_
  induction' hn : y.length using Nat.strongInductionOn with n ih generalizing y
  induction y using (measure List.length).wf.induction with | _ y ih => ?_
  sorry

But I guess this can't be solved by an induction principle.

llllvvuu (Jun 05 2024 at 22:53):

Joachim Breitner said:

Also, it may not be needed to have a separate nil and cons case, and a single case might be nicer.

I guess if one wanted to mimic the multiset API one could rename List.strongRec and List.suffix_induction to List.strongInduction and List.caseStrongInductionOn respectively, and also add List.strongInductionOn (funny thing: this is spelled differently in docs#Nat.caseStrongInductionOn, docs#Multiset.case_strongInductionOn, and docs#Finset.case_strong_induction_on). Though I agree the non-case variant is often better / there are very few usages of the case variants in mathlib.

Marcus Rossel (Jun 06 2024 at 11:33):

Side note: I've asked the same question before here: https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/List.20strong.20induction

llllvvuu (Jun 07 2024 at 08:54):

(deleted)

llllvvuu (Jun 07 2024 at 09:30):

I was just about to propose it in Batteries anyway thinking it did something that the Nat version didn't, but then I realized I got confused, so I closed it. Maybe it could still be of interest in Mathlib for the sake of completeness / showing up in search results (interestingly, docs#Finset.strongInductionOn does actually get used a few times), if not useful in Batteries.

But I did put in a proposal to add Joachim Breitner's α → Nat idea from above.


Last updated: May 02 2025 at 03:31 UTC