Zulip Chat Archive

Stream: lean4

Topic: WF Induction bug


Peter Nelson (Nov 27 2023 at 01:41):

This isn't quite as minimized as it could be (the problem has nothing to do with Finset), but the following looks like a bug.

The only difference between foo1 and foo2 is that foo1 uses an indented tactic line to prove h_lt, and foo2 uses :=. The former fails with a generic WF induction error, and the latter works.

import Mathlib.Data.Finset.Card

def P : Finset   Prop := sorry

theorem hP {s : Finset } (hs : ¬ P s) :  t, t  s  ¬ P t  := sorry

theorem foo1 (s : Finset ) : P s := by
  by_contra h
  obtain t, ht, hPt := hP h
  have h_lt : t.card < s.card
  · exact Finset.card_lt_card ht
  exact hPt <| foo1 t
termination_by _ => s.card
-- failed to prove termination, possible solutions:
--   - Use `have`-expressions to prove the remaining goals
--   - Use `termination_by` to specify a different well-founded relation
--   - Use `decreasing_by` to specify your own tactic for discharging this kind of goal

theorem foo2 (s : Finset ) : P s := by
  by_contra h
  obtain t, ht, hPt := hP h
  have h_lt : t.card < s.card := Finset.card_lt_card ht
  exact hPt <| foo2 t
termination_by _ => s.card
-- works fine

Joachim Breitner (Nov 27 2023 at 08:48):

With decreasing_by simp_wf we can see the goals in both cases. This is the goal that fails

s: Finset 
a:  (y : Finset ), (invImage (fun a => Finset.card a) instWellFoundedRelation).1 y s  P y
h: ¬P s
t: Finset 
h: t  s  ¬P t
ht: t  s
hPt: ¬P t
 Finset.card t < Finset.card s

and this is the corresponding goal that succeeds

s: Finset 
a:  (y : Finset ), (invImage (fun a => Finset.card a) instWellFoundedRelation).1 y s  P y
h: ¬P s
t: Finset 
h: t  s  ¬P t
ht: t  s
hPt: ¬P t
h_lt: Finset.card t < Finset.card s
 Finset.card t < Finset.card s

So it loses the l_lt premise somehow.

Joachim Breitner (Nov 27 2023 at 08:50):

With set_option trace.Elab.definition.wf true we see more:

 foo1 :=
    WellFounded.fix (invImage (fun a => Finset.card a) instWellFoundedRelation).2 fun s a =>
      Classical.byContradiction fun h =>
        Exists.casesOn (hP h) fun t h =>
          And.casesOn h fun ht hPt =>
            hPt (a t (Eq.mpr (id Std.Classes.Order._auxLemma.4) (sorryAx (Finset.card t < Finset.card s) true)))

vs.

foo2 :=
    WellFounded.fix (invImage (fun a => Finset.card a) instWellFoundedRelation).2 fun s a =>
      Classical.byContradiction fun h =>
        Exists.casesOn (hP h) fun t h =>
          And.casesOn h fun ht hPt =>
            let_fun h_lt := Finset.card_lt_card ht;
            hPt (a t (Eq.mpr (id Std.Classes.Order._auxLemma.4) (sorryAx (Finset.card t < Finset.card s) true)))

so the let_fun is dropped somewhere along the way, and it seems to be happening before it reaches the code that deals with recursion.

Maybe someone who knows more about the implementation of have can spot the issue. In any case, feel free to open an issue. Maybe you can minimize it further; it can also be very helpful to know when the issue disappears.

Mario Carneiro (Nov 27 2023 at 09:09):

this is a known issue, you can work around it by putting the have in the same term as the exact

Joachim Breitner (Nov 27 2023 at 09:59):

Is there a lean issue for it?


Last updated: Dec 20 2023 at 11:08 UTC