Zulip Chat Archive
Stream: mathlib4
Topic: simp non-confluence
Michael Stoll (Jan 05 2024 at 21:44):
import Mathlib
example (f : Polynomial ℤ) : Polynomial.coeff (Polynomial.C (Polynomial.coeff f 0)) 1 = 0 := by
-- simp? says simp only [eq_intCast]
-- ⊢ Polynomial.coeff (↑(Polynomial.coeff f 0)) 1 = 0
simp? [-eq_intCast] says simp only [Polynomial.coeff_C_succ] -- success!
It looks like some simp
lemmas are missing to make things confluent here.
Ruben Van de Velde (Jan 05 2024 at 22:51):
Michael Stoll (Jan 06 2024 at 10:52):
@Alex J. Best Maybe you can add a couple more lemmas?
Kevin Buzzard (Mar 05 2025 at 23:06):
I found another simp non-confluence issue.
import Mathlib
example (x : ℝ) : (Real.exp x : ℂ).re = Real.exp x := by
/-
`simp` gives
⊢ (Complex.exp ↑x).re = Real.exp x
`simp?` gives
Try this: simp only [Complex.ofReal_exp]
-/
simp [-Complex.ofReal_exp] -- success
Here is my proposed fix:
import Mathlib
@[simp] theorem Complex.re_exp (x : ℝ) : (Complex.exp x).re = Real.exp x := by
simp [← Complex.ofReal_exp]
example (x : ℝ) : (Real.exp x : ℂ).re = Real.exp x := by
simp -- now works
Is this the correct fix? Should we also have
-- example (x : ℝ) : (Real.sin x : ℂ).re = Real.sin x := by simp -- :-(
@[simp] theorem Complex.re_sin (x : ℝ) : (Complex.sin x).re = Real.sin x := by
simp [← Complex.ofReal_sin]
example (x : ℝ) : (Real.sin x : ℂ).re = Real.sin x := by simp -- :-)
-- example (x : ℝ) : (Real.cos x : ℂ).re = Real.cos x := by simp -- :-(
@[simp] theorem Complex.re_cos (x : ℝ) : (Complex.cos x).re = Real.cos x := by
simp [← Complex.ofReal_cos]
example (x : ℝ) : (Real.cos x : ℂ).re = Real.cos x := by simp -- :-)
? Are the names OK? Are there any more functions for which we need these (if my diagnosis and fix is OK)?
Kevin Buzzard (Mar 05 2025 at 23:09):
One sign that this is going in the right direction is that with these lemmas added we have
import Mathlib
@[simp] theorem Complex.re_exp (x : ℝ) : (Complex.exp x).re = Real.exp x := by
simp [← Complex.ofReal_exp]
@[simp] theorem Complex.re_sin (x : ℝ) : (Complex.sin x).re = Real.sin x := by
simp [← Complex.ofReal_sin]
@[simp] theorem Complex.re_cos (x : ℝ) : (Complex.cos x).re = Real.cos x := by
simp [← Complex.ofReal_cos]
example : (-1 : ℝ) ^ (1 / 3 : ℝ) = 1 / 2 := by
simp [Real.rpow_def, Complex.cpow_def_of_ne_zero, Complex.log_neg_one, -Complex.ofReal_inv]
rw [mul_right_comm, Complex.exp_mul_I]
norm_cast
rw [← div_eq_mul_inv]
simp
whereas my previous effort was far worse. The -Complex.ofReal_inv
is to work around this issue.
Eric Wieser (Mar 05 2025 at 23:12):
These should be called re_exp_ofReal
etc, not just re_exp
Eric Wieser (Mar 05 2025 at 23:12):
Is docs#Complex.ofReal_exp just a bad simp lemma?
Kevin Buzzard (Mar 05 2025 at 23:14):
It's definitely a good norm_cast
lemma (and my understanding of norm_cast
is that it should be this way around). My experiments with ofReal_cos
was that it was rewritten forwards more than it was rewritten backwards, but it's still rewritten backwards a fair few times (of course it might be being rewritten forwards many times in simp
s). I think there is no good direction really, this is exactly why we have both norm_cast
and push_cast
.
Kevin Buzzard (Mar 05 2025 at 23:17):
@Mario Carneiro you understand this stuff well, do you have an opinion on whether docs#Complex.ofReal_exp should be a simp lemma? I remember discussing this stuff with you before norm_cast
existed and you had a clear opinion that it wasn't simp
s job to do norm_cast
and push_cast
and we needed a new tactic (and this is why we got them). But are you clear on how the tagging should work?
Kevin Buzzard (Mar 05 2025 at 23:18):
Does my fix not scale? Is that the issue?
Mario Carneiro (Mar 05 2025 at 23:23):
it seems good to me, that is indeed the lemma you need to fix the non-confluence
Eric Wieser (Mar 05 2025 at 23:24):
Yes, the new lemmas is the right lemma to have assuming the original one should be simp
Eric Wieser (Mar 05 2025 at 23:25):
Do you also think the existing lemma is correct in being tagged simp
, Mario?
Mario Carneiro (Mar 05 2025 at 23:25):
regarding which direction is right, I think the precedent from nat->int casts is that simp pushes casts to the leaves, so Complex.ofReal_exp
makes sense from that perspective. But .re
is a function which goes the other way, making it play a similar role to the int less equal operation which originally motivated norm_cast
Mario Carneiro (Mar 05 2025 at 23:27):
Actually I guess the real problem is that we don't have a simp lemma for (Complex.sin z).re
, presumably because the formula is too much of a mess to be a good idea in general
Mario Carneiro (Mar 05 2025 at 23:28):
if it wasn't for that, .re
would get evaluated recursively over a complex number expression (i.e. also getting pushed to the leaves)
Kevin Buzzard (Mar 05 2025 at 23:52):
and and you can get it from that, it shouldn't be too bad.
Kevin Buzzard (Mar 05 2025 at 23:54):
Knuth would rather we said and but this was one design decision in which I was never mad keen on.
Mario Carneiro (Mar 05 2025 at 23:55):
I don't mean we don't have a theorem, I assume we do but it's not a simp lemma
Mario Carneiro (Mar 05 2025 at 23:55):
also I was thinking specifically about sin/cos, not exp
Kevin Buzzard (Mar 05 2025 at 23:55):
yeah but they're defined in terms of exp as you well know
Mario Carneiro (Mar 05 2025 at 23:55):
exp has less messy equations indeed
Mario Carneiro (Mar 05 2025 at 23:56):
what I mean is that if you had a (sin z).re
in your goal you might not like lean expanding it out to exponentials
Mario Carneiro (Mar 05 2025 at 23:56):
which is why it should be a theorem but not a simp lemma, so you choose to use it explicitly
Kevin Buzzard (Mar 05 2025 at 23:58):
I think Re(sin(z)) is (modulo errors) cosh(Im(z))sin(Re(z))
Kevin Buzzard (Mar 05 2025 at 23:58):
which at least passes the sanity check when z is real, as cosh(0)=1
Mario Carneiro (Mar 06 2025 at 00:00):
Is (z * w).re
a simp lemma?
Mario Carneiro (Mar 06 2025 at 00:00):
I guess these formulas are not much worse than the one for multiplication
Aaron Liu (Mar 06 2025 at 00:01):
Mario Carneiro said:
Is
(z * w).re
a simp lemma?
yes (docs#Complex.mul_re)
Mario Carneiro (Mar 06 2025 at 00:02):
If you had Re(sin(z)) = cosh(Im(z))sin(Re(z)) as a simp lemma, then Re(sin(x)) would eventually work its way to sin(x)
Kevin Buzzard (Mar 06 2025 at 00:03):
Aaron Liu said:
Mario Carneiro said:
Is
(z * w).re
a simp lemma?yes (docs#Complex.mul_re)
That was part of my first mathlib PR ;-) (which you reviewed!)
Jireh Loreaux (Mar 06 2025 at 12:02):
That simp lemma has bothered me on many occasions. :laughing:
Yaël Dillies (Mar 06 2025 at 12:05):
Me too! :grinning:
Kevin Buzzard (Mar 06 2025 at 12:45):
Well I can't really claim that I knew what I was doing in 2017. IIRC it's really useful to have it as a "temporary simp
lemma" in order to prove e.g. multiplication on is associative by checking real and imaginary parts (you want the proof to be ext; simp; ring
). But then later on it might become a burden?
Last updated: May 02 2025 at 03:31 UTC