Zulip Chat Archive

Stream: mathlib4

Topic: Adaptation note in DyckWord


Jeremy Tan (May 01 2025 at 10:14):

@Michael Rothgang

include h in
lemma firstReturn_pos : 0 < p.firstReturn := by
  by_contra! f
  rw [Nat.le_zero, firstReturn, findIdx_eq] at f
  #adaptation_note
  /--
  If we don't swap, then the second goal is dropped after completing the first goal.
  What's going on?
  -/
  swap
  · rw [length_range, length_pos_iff]
    exact toList_ne_nil.mpr h
  · rw [getElem_range] at f
    simp at f
    rw [ p.cons_tail_dropLast_concat h] at f
    simp at f

In trying to remove this adaptation note (#24492) I have found out the following.

Jeremy Tan (May 01 2025 at 10:15):

Just before the findIdx_eq rewrite the goal state is the following

p : DyckWord
h : p  0
f : findIdx (fun i  decide (count U (List.take (i + 1) p) = count D (List.take (i + 1) p))) (range (p).length) = 0
 False

Jeremy Tan (May 01 2025 at 10:15):

After the rewrite:

p : DyckWord
h : p  0
f : findIdx (fun i  decide (count U (List.take (i + 1) p) = count D (List.take (i + 1) p))) (range (p).length) = 0
f : decide (count U (List.take ((range (p).length)[0] + 1) p) = count D (List.take ((range (p).length)[0] + 1) p)) =
    true 
   (j : ) (hji : j < 0),
    decide (count U (List.take ((range (p).length)[j] + 1) p) = count D (List.take ((range (p).length)[j] + 1) p)) =
      false
 False
p : DyckWord
h : p  0
f : findIdx (fun i  decide (count U (List.take (i + 1) p) = count D (List.take (i + 1) p))) (range (p).length) = 0
 0 < (range (p).length).length

Jeremy Tan (May 01 2025 at 10:18):

By contrast, if I write it like

include h in
lemma firstReturn_pos : 0 < p.firstReturn := by
  rw [ not_le, Nat.le_zero, firstReturn, findIdx_eq, getElem_range]
  · simp
    rw [ p.cons_tail_dropLast_concat h]
    simp
  · rwa [length_range, length_pos_iff, toList_ne_nil]

no spurious f✝ hypothesis is generated. Why would f✝ be generated in the first case?

Jeremy Tan (May 01 2025 at 10:24):

Now suppose I have

include h in
lemma firstReturn_pos : 0 < p.firstReturn := by
  by_contra! f
  have k : 0 < (range p.toList.length).length := by
    rw [length_range, length_pos_iff]
    exact toList_ne_nil.mpr h
  rw [Nat.le_zero, firstReturn, findIdx_eq k] at f
  rw [getElem_range] at f
  simp at f
  rw [ p.cons_tail_dropLast_concat h] at f
  simp at f

Then when the findIdx rewrite happens, f jumps from above k in the hypothesis list to below it:

p : DyckWord
h : p  0
f : findIdx (fun i  decide (count U (List.take (i + 1) p) = count D (List.take (i + 1) p))) (range (p).length) = 0
k : 0 < (range (p).length).length
 False
p : DyckWord
h : p  0
k : 0 < (range (p).length).length
f : decide (count U (List.take ((range (p).length)[0] + 1) p) = count D (List.take ((range (p).length)[0] + 1) p)) =
    true 
   (j : ) (hji : j < 0),
    decide (count U (List.take ((range (p).length)[j] + 1) p) = count D (List.take ((range (p).length)[j] + 1) p)) =
      false
 False

Is this a bug?

Damiano Testa (May 01 2025 at 15:16):

The ordering of the hypotheses is, initially, "chronological". Once you start modifying them, the hypothesis that was just modified becomes last. This means that the answer to your last question may be that this is not a bug, but expected behaviour.

Damiano Testa (May 01 2025 at 15:17):

As for disappearing goals, that is usually a consequence of a goal solved by unification, while Lean realises what the ingredients of a proof are in a separate branch.

Damiano Testa (May 01 2025 at 15:18):

So, Lean figures out what the relevant metavariables are in one branch and then propagates this information to the branches that were created when the first one was also created, now has the information of what they need to be and, sometimes, this is information enough to actually solve them.


Last updated: May 02 2025 at 03:31 UTC