Zulip Chat Archive

Stream: general

Topic: simp_rw weirdness


Kevin Buzzard (Jul 28 2022 at 09:48):

A student just came to me with the following question, which I'm completely bamboozled by:

import tactic
import data.real.basic

example {a :   } {t : } (ha :  (ε : ), 0 < ε 
  ( (B : ),  (n : ), B  n  |a n - t| < ε)) :
   (ε : ), 0 < ε  ( (B : ),  (n : ), B  n  |t - a n| < ε) :=
begin
  -- simp_rw [abs_sub_comm], -- ??
  -- convert ha,
  simp_rw abs_sub_comm at ha, -- ??
  exact ha,
end

They can't use rw abs_sub_comm on either the goal or ha, presumably because it's under a binder. But why does simp_rw only work on ha and not on the goal? When applied to the goal you get the error simplify tactic failed to simplify despite everything looking completely formally the same to me.

Probably unrelated: it would be nice if convert ha using 37 turned the goal into |a n - t| = |t - a n| but in practice it makes essentially no progress. I'm also slightly surprised by this...

Damiano Testa (Jul 28 2022 at 10:51):

This does not answer much, but the very verbose congrm (∀ ε : ℝ, 0 < ε → ∃ B : ℕ, ∀ n : ℕ, B ≤ n → (_ < ε)) does what convert ha alone should have done:

example {a :   } {t : } (ha :  (ε : ), 0 < ε 
  ( (B : ),  (n : ), B  n  |a n - t| < ε)) :
   (ε : ), 0 < ε  ( (B : ),  (n : ), B  n  |t - a n| < ε) :=
begin
  convert ha,
  congrm ( ε : , 0 < ε   B : ,  n : , B  n  (_ < ε)),
  exact abs_sub_comm _ _,
end

Damiano Testa (Jul 28 2022 at 10:57):

Maybe this is a cunning plot by Patrick to get everyone to use filters.

Damiano Testa (Jul 28 2022 at 11:10):

Actually, this is some further weirdness: change the order of the subtraction and simp_rw works on the target!

example {a :   } {t : } (ha :  (ε : ), 0 < ε 
  ( (B : ),  (n : ), B  n  |a n - t| < ε)) :
   (ε : ), 0 < ε  ( (B : ),  (n : ), B  n  |a n - t| < ε) :=  -- note that this is the same as the hypothesis `ha`
begin
  simp_rw [abs_sub_comm], -- works!
  simp_rw [abs_sub_comm], -- but doing it twice does not!
end

Maybe it is because a is a function and some unification does not work on it, but it does on t?

Damiano Testa (Jul 28 2022 at 11:12):

here is a shorter failuer:

example {a :   } {t : } (n : ) :
   (ε : ), |a n - t| < ε :=
begin
  simp_rw [abs_sub_comm], -- works
  simp_rw [abs_sub_comm], -- fails
end

Kevin Buzzard (Jul 28 2022 at 11:25):

Actually, this is some further weirdness: change the order of the subtraction and simp_rw works on the target!

Yes, in my original post I noted that simp_rw works on ha but not the goal. It's order-dependent. Your example is a clearer version of this.

Damiano Testa (Jul 28 2022 at 11:37):

Ah, I had read your comment as saying that goals and hypotheses are dealt with differently. They might, but not in this case! :smile:

Damiano Testa (Jul 28 2022 at 11:45):

I may stop after this, it seems that the order in which the variables are declared (and not their type) matters:

example {a b :   } :
   n : , |a n + b n| = 0 :=
begin
  simp only [add_comm], --fails
end

example {b a :   } :  --  note that `b` is declared first here
   n : , |a n + b n| = 0 :=
begin
  simp only [add_comm], -- works
  simp only [add_comm], -- fails
end

Kevin Buzzard (Jul 28 2022 at 11:47):

Oh nice! What the heck is going on?

Damiano Testa (Jul 28 2022 at 11:48):

It is really puzzling: whenever I think that it might be something, it turns out that it isn't! Very cool example!

Damiano Testa (Jul 28 2022 at 12:03):

Btw, it is really simp that has this behaviour: simp_rw works until it calls simp and then simp fails. I updated the last example to use simp only and still displaying the same failure pattern.

Also,

  conv in |_| { rw [add_comm, add_comm, add_comm, add_comm, add_comm, add_comm] },

works, but, depending on the parity of the add_comms, the simp may or may not work! :upside_down:

Johan Commelin (Jul 28 2022 at 12:08):

Isn't this some sort of loop detection built in to simp?

Johan Commelin (Jul 28 2022 at 12:09):

Because simp [add_comm] would loop like crazy. But (iirc) it decides to only rewrite when it ends up sorting subexpressions, for some random ordering of subexpressions. And this ordering is probably influenced by de Bruijn indices, which are influenced by the order in which vars are declared.

Damiano Testa (Jul 28 2022 at 12:16):

Johan, your explanation fits with all that I have been observing!

Johan Commelin (Jul 28 2022 at 12:18):

And this is a sensible default for simp. But since simp_rw only does a single simp-pass, it would make sense if that loop-detection were disabled.

Eric Wieser (Jul 28 2022 at 12:44):

simp_rw does not make a single pass through

Eric Wieser (Jul 28 2022 at 12:44):

simp only [add_comm] { single_pass := tt } does though

Johan Commelin (Jul 28 2022 at 12:44):

Ooh :bulb: So what does simp_rw do? I guess I don't have an accurate model for how simp_rw is different from simp.

Eric Wieser (Jul 28 2022 at 12:45):

src#tactic.interactive.simp_rw

Eric Wieser (Jul 28 2022 at 12:45):

It just calls simp only [lemma] in sequence.

Johan Commelin (Jul 28 2022 at 12:47):

Yep, I get it now.


Last updated: Dec 20 2023 at 11:08 UTC