Zulip Chat Archive

Stream: new members

Topic: modifying exists prop


Matt Diamond (Aug 01 2022 at 02:23):

Is there a simple way to rewrite (or map over) the prop within an existential without using conv?

Like if I have a hypothesis ∃ (c : ℕ), b = 2 * c and I want to rewrite it to ∃ (c : ℕ), 2 * c = b, is there a simple way to do that? I know it can be accomplished by going into conv mode (or just unpacking it with cases and operating on the contents directly), but I was wondering if there was an even easier way to do it.

I tried messing around with exists_congr and eq_comm but I couldn't get it to work that well.

Matt Diamond (Aug 01 2022 at 02:49):

it would be cool if I could do something like h.map eq.symm (where h is the name of an existential hypothesis)

Edit: that seems to be what exists_imp_exists is, so I just need to make it work with eq.symm

Matt Diamond (Aug 01 2022 at 03:11):

hmm, it looks like exists_congr and exists_imp_exists actually work as long as they're given explicit typing, though that can be kind of cumbersome

Xavier Roblot (Aug 01 2022 at 03:19):

It looks like simp_rw eq_comm works here.

Matt Diamond (Aug 01 2022 at 03:23):

ah nice!

Matt Diamond (Aug 01 2022 at 03:24):

I knew there had to be a trick somewhere

Xavier Roblot (Aug 01 2022 at 03:24):

simp_rw is really powerful and can work in many cases where rw does not.

Matt Diamond (Aug 01 2022 at 03:37):

@Xavier Roblot I'm running into an issue with this... here's a mwe:

import tactic.basic

def b :  := 1

example (h :  (c : ), b = 2 * c) :  (c : ), 2 * c = b := begin
  simp_rw [eq_comm],
  exact h
end

example (h :  (c : ), 2 * c = b) :  (c : ), b = 2 * c := begin
  simp_rw [eq_comm],
  exact h
end

The first works but the second fails, and the only difference is the ordering of the left and right sides of the equation.

(Not a big deal as I can just rewrite from the other direction, of course.)

Xavier Roblot (Aug 01 2022 at 03:50):

Well, you reached (quite rapidly) the limits of my knowledge of Lean. Still, I think what happens is that Lean has some rules to decide if an expression is simpler than another one so that the simp process converges and does end up in an infinite loop. Thus, b = 2 * c is simpler than 2 * c = b and therefore the simplification part is failing in your second example. As you noticed, in this case, you can just do simp_rw [eq_comm] at h instead and the proof works then.
I guess somebody with a better knowledge of Lean can confirm or correct my explanation.

Alex J. Best (Aug 01 2022 at 03:56):

Another workaround is to give lean a bit more information

example (h :  (c : ), 2 * c = b) :  (c : ), b = 2 * c := begin
  simp_rw [(eq_comm : _ = _ * _  _)],
  exact h
end

I guess this breaks the symmetry so that it doesn't do the infinite loop thing Xavier mentions.

Matt Diamond (Aug 01 2022 at 04:22):

Thanks for the replies!

For the record, it didn't enter an infinite loop... it completed with the error message simplify tactic failed to simplify. Based on the fact that Alex's version works, I think it was just a case of being unable to match (unify?) eq_comm with the existing equation without further hints.

Alex J. Best (Aug 01 2022 at 04:39):

What Xavier mentioned is the mechanism for avoiding infinite loops, simp makes a somewhat arbitrary decision that one way round of your equality is the right one, and refuses to simplify the other way round as it thinks your expression is the best possible already. simp_rw is fairly close to calling simp only.

Alex J. Best (Aug 01 2022 at 04:41):

If you do set_option trace.simplify you can see a bunch of what is going on under the hood:

set_option trace.simplify true
example (h :  (c : ), 2 * c = b) :  (c : ), b = 2 * c := begin
  simp_rw [eq_comm],
  -- simp_rw [(eq_comm : _ = _ * _ ↔ _)],
  exact h
end

tells us a bunch of stuff including

[simplify.perm] perm rejected: 2 * c = b !< b = 2 * c

Alex J. Best (Aug 01 2022 at 04:42):

Rather amusingly instead simp_rw [(eq_comm : _ ↔ _)], does seem to enter an infinite loop...

Damiano Testa (Aug 01 2022 at 05:11):

On the topic of loops in simp/simp_rw, there was also this recent thread.

Eric Wieser (Aug 01 2022 at 08:21):

Does h.imp eq.symm work?

Kevin Buzzard (Aug 01 2022 at 08:57):

This works:

example (h :  (c : ), 2 * c = b) :  (c : ), b = 2 * c := begin
  simp_rw [@eq_comm _ b],
  exact h
end

Patrick Johnson (Aug 01 2022 at 14:49):

@eq_comm _ b doesn't work if both sides of the equality contain bound variables:

import tactic

example {α β : Type*} {f g : α  β}
  (h :  (x : α), g x = f x) :  (x : α), f x = g x :=
begin
  simp_rw [@eq_comm (f _) (g _)], -- fails
  simp_rw [(eq_comm : f _ = g _  _)], -- works
  exact h,
end

Eric Rodriguez (Aug 01 2022 at 14:56):

There's also docs#Exists.imp, which I think trivialises this a lpt


Last updated: Dec 20 2023 at 11:08 UTC