Zulip Chat Archive

Stream: general

Topic: dependent rewriting


Simon Hudon (Oct 05 2020 at 16:01):

I just wrote a tactic for dependent rewriting. Is there any interest in having it in mathlib?

Here is what it does:

def foo' (i j : ) (v : vector  j) (h : i  j) : Prop := i  j

example (i : ) (j : ) (v : vector  (j - i)) (h : i  j - i) (h' :  i, j - i = i) :
  foo' i (j - i) v h :=
begin
  dep_rw h',
--   i j : ℕ,
-- v : vector ℤ (j - i),
-- h : i ≤ j - i,
-- h' : ∀ (i : ℕ), j - i = i
-- ⊢ foo' i i (cast _ v) (cast _ h)
end

f has a dependent type and rewriting j requires to add a cast to v and h at the same time which is pretty tricky to do. That's why dependent rewriting can be handy in those situations. The norm in mathlib is to try to avoid this kind dependent function type. When you have to use them though, it's good to have a tactic that accommodates them.

Reid Barton (Oct 05 2020 at 16:37):

Yes, this would be extremely useful.

Reid Barton (Oct 05 2020 at 16:38):

Would it be practical to make rw itself do this?

Simon Hudon (Oct 05 2020 at 16:44):

I don't believe so. I think rw does its job by finding occurrences of the lhs and abstracting them so you don't need a complex tree rewriting. In order to make it dependent, I need to use the congruence lemmas that congr generates

Simon Hudon (Oct 05 2020 at 16:44):

Unless you use dependent rewriting as a sort of plan B if the motive is not type correct

Reid Barton (Oct 05 2020 at 17:02):

If it's not practical to make rw do this automatically then perhaps we can at least change the "motive is not type correct" error to mention dep_rw.

Simon Hudon (Oct 05 2020 at 17:03):

That makes sense. We should probably get it into core in that case

Bhavik Mehta (Oct 06 2020 at 16:45):

When I get the motive error, I sometimes think about using simp_rw instead. How does this differ from that?

Simon Hudon (Oct 06 2020 at 16:47):

Actually, I'm not familiar with simp_rw. Does it insert cast for you?

Bhavik Mehta (Oct 06 2020 at 16:49):

I don't think it does - taking a closer look I think your tactic is just cleverer than simp_rw in terms of working with dependent functions

Yury G. Kudryashov (Oct 06 2020 at 17:05):

simp_rw is a sequence of simp only

Yury G. Kudryashov (Oct 06 2020 at 17:06):

Someone's it's useful to avoid loops

Kevin Buzzard (Oct 06 2020 at 18:01):

Oh that's the point of it!

Bhavik Mehta (Oct 07 2020 at 00:45):

I sometimes use it when my goal state has a term which contains a proof (eg f x h, where x is data and h is a proof involving x), and I want to rewrite something like x = y - rw gives everyone's favourite motive error but simp_rw sometimes works

Yakov Pechersky (Oct 07 2020 at 17:46):

Would this rw tactic help here? Neither rw not simp_rw work:

import data.vector2

variables {α β : Type}
variable {n : }

def vector.last (v : vector α (n + 1)) : α := v.nth (fin.last n)

lemma vector.last_def (v : vector α (n + 1)) : v.last = v.nth (fin.last n) := rfl

lemma vector.to_list_reverse (v : vector α n) : v.reverse.to_list = v.to_list.reverse := rfl

lemma vector.reverse_nth_zero (v : vector α (n + 1)) : v.reverse.head = v.last :=
begin
  rw vector.nth_zero,
  rw vector.last_def,
  rw vector.nth_eq_nth_le,
  rw vector.nth_eq_nth_le,
  simp_rw vector.to_list_reverse,
  simp_rw [fin.val_eq_coe, fin.coe_last, fin.coe_zero],
  have : n = v.to_list.length - 1,
    { simp only [nat.add_succ_sub_one, add_zero, vector.to_list_length] },
  simp_rw [this], --breaks here
  sorry,
end

Simon Hudon (Oct 07 2020 at 18:35):

Can you show what the goal is at the point where it breaks?

Yakov Pechersky (Oct 07 2020 at 18:42):

lemma vector.reverse_nth_zero (v : vector α (n + 1)) : v.reverse.head = v.last :=
begin
  rw vector.nth_zero,
  rw vector.last_def,
  rw vector.nth_eq_nth_le,
  rw vector.nth_eq_nth_le,
  simp_rw vector.to_list_reverse,
  simp_rw [fin.val_eq_coe, fin.coe_last, fin.coe_zero],
  have : n = v.to_list.length - 1,
    { simp only [nat.add_succ_sub_one, add_zero, vector.to_list_length] },
    /-
invalid simplification lemma 'this' (use command 'set_option trace.simp_lemmas true' for more details)
state:
α : Type,
n : ℕ,
v : vector α (n + 1),
this : n = v.to_list.length - 1
⊢ v.to_list.reverse.nth_le 0 _ = v.to_list.nth_le n _
-/

  simp_rw [this], --breaks here
  sorry,
end

Yakov Pechersky (Oct 07 2020 at 18:43):

I do have a proof for the lemma though, avoiding the issue:

lemma vector.reverse_nth_zero (v : vector α (n + 1)) : v.reverse.head = v.last :=
begin
  have : 0 = v.to_list.length - 1 - n,
    { simp only [nat.add_succ_sub_one, add_zero, vector.to_list_length, nat.sub_self,
                 list.length_reverse] },
  rw [vector.nth_zero, vector.last_def, vector.nth_eq_nth_le, vector.nth_eq_nth_le],
  simp_rw [vector.to_list_reverse, fin.val_eq_coe, fin.coe_last, fin.coe_zero, this],
  rw list.nth_le_reverse,
end

Reid Barton (Oct 07 2020 at 18:44):

Yes, this is exactly the sort of situation in question--you can't simply rewrite n because the type of the following proof depends on it

Yakov Pechersky (Oct 07 2020 at 18:47):

But rewriting 0 does work, even though the nth_le 0 _ proof relies on it too.

Simon Hudon (Oct 07 2020 at 18:49):

Do you rewrite it to something definitionally equal?

Yakov Pechersky (Oct 07 2020 at 18:52):

I don't think 0 = v.to_list.length - 1 - n are defeq, since I require that 5 lemma simp only to prove that.

Reid Barton (Oct 07 2020 at 18:54):

I think there are weird conditions where rewriting can work even in the presence of proofs because the way rw works means that it also rewrites the proof

Yakov Pechersky (Oct 07 2020 at 18:56):

Totally. The other steps in simp_rw are able to do that rewrite too. Interestingly, the last rw list.nth_le_reverse in my functional proof will not work if I use simp_rw instead.

Eric Wieser (Oct 27 2020 at 18:55):

Would dep_rw help with my goal in https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Difficulty.20with.20dependent.20rewrites/near/214686669?

Simon Hudon (Oct 27 2020 at 21:51):

I think it might

Yakov Pechersky (Oct 27 2020 at 21:59):

Another vector use case. Often, rw <- vector.cons_head_tail v fails since v is in proofs. But simp_rw <- vector.cons_head_tail v results in deep recursion.

Mario Carneiro (Oct 27 2020 at 22:02):

use the single_pass option perhaps?

Mario Carneiro (Oct 27 2020 at 22:04):

actually it might be easier to do rcases on \exists h t, v = h :: t if you are trying to eliminate it

Yakov Pechersky (Oct 27 2020 at 22:20):

Great advice, thanks!


Last updated: Dec 20 2023 at 11:08 UTC