Zulip Chat Archive
Stream: lean4
Topic: simp [X] fails, rw [X] works
Kevin Buzzard (May 16 2023 at 22:21):
Porting RingTheory.TensorProduct
I noticed that there were a few times when simp only [A,B,C]
in mathlib3 had to be changed to simp only [A], rw [B], simp only [C]
. This is kind of annoying because debugging a failing simp
call is slow and you never know what isn't firing if you don't understand the mathematics properly.
So here's an example on the RingTheory.TensorProduct
branch !4#4004, which I know isn't ideal, but it at least indicates how far I've got with debugging. The simp
call which fails and needs to be turned into a rw
is here, and here's what's going on:
import Mathlib.RingTheory.TensorProduct
variable {R : Type} [CommSemiring R]
variable {A : Type} [Semiring A] [Algebra R A]
variable {B : Type} [Semiring B] [Algebra R B]
open TensorProduct -- for notation
example (x y z : A ⊗[R] B) : x * (y + z) = x * y + x * z := by
simp only [mul_add] -- works fine
done
example (x y z : A ⊗[R] B) : x *
-- `(y + z)` but with a different `+`
(@HAdd.hAdd.{0, 0, 0}
(@TensorProduct.{0, 0, 0} R _ A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} A (@Semiring.toNonAssocSemiring.{0} A _)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} B (@Semiring.toNonAssocSemiring.{0} B _)))
(@Algebra.toModule.{0, 0} R A _ _ _) (@Algebra.toModule.{0, 0} R B _ _ _))
(@TensorProduct.{0, 0, 0} R _ A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} A (@Semiring.toNonAssocSemiring.{0} A _)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} B (@Semiring.toNonAssocSemiring.{0} B _)))
(@Algebra.toModule.{0, 0} R A _ _ _) (@Algebra.toModule.{0, 0} R B _ _ _))
(@TensorProduct.{0, 0, 0} R _ A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} A (@Semiring.toNonAssocSemiring.{0} A _)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} B (@Semiring.toNonAssocSemiring.{0} B _)))
(@Algebra.toModule.{0, 0} R A _ _ _) (@Algebra.toModule.{0, 0} R B _ _ _))
(@instHAdd.{0}
(@TensorProduct.{0, 0, 0} R _ A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} A (@Semiring.toNonAssocSemiring.{0} A _)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} B (@Semiring.toNonAssocSemiring.{0} B _)))
(@Algebra.toModule.{0, 0} R A _ _ _) (@Algebra.toModule.{0, 0} R B _ _ _))
(@AddZeroClass.toAdd.{0}
(@TensorProduct.{0, 0, 0} R _ A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} A (@Semiring.toNonAssocSemiring.{0} A _)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} B (@Semiring.toNonAssocSemiring.{0} B _)))
(@Algebra.toModule.{0, 0} R A _ _ _) (@Algebra.toModule.{0, 0} R B _ _ _))
(@TensorProduct.addZeroClass.{0, 0, 0} R _ A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} A (@Semiring.toNonAssocSemiring.{0} A _)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid.{0} B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} B (@Semiring.toNonAssocSemiring.{0} B _)))
(@Algebra.toModule.{0, 0} R A _ _ _) (@Algebra.toModule.{0, 0} R B _ _ _))))
y z) = x * y + x * z := by
-- Goal is `x * (y + z) = x * y + x * z`
simp only [mul_add] -- does nothing
rw [mul_add] -- works
-- Alternative finish:
-- change x * (y + z) = x * y + x * z -- changes `+` syntactically but not definitionally
-- simp only [mul_add] -- now works
done
In the PR, somehow addition has managed to become a noncanonical addition, which is what seems to stop simp
from working, but doesn't stop rw
. Hopefully someone will come up with a better example but maybe is this already enough of a clue for people to explain what's going on.
Eric Wieser (May 16 2023 at 22:43):
Uh oh; does this mean simp
is keying on the instance arguments in the DiscrTree?
Eric Wieser (May 16 2023 at 22:47):
RingTheory.TensorProduct
not LinearAlgebra.TensorProduct
, right?
Kevin Buzzard (May 16 2023 at 22:50):
Thanks -- fixed
Eric Wieser (May 16 2023 at 22:50):
simp only [(mul_add)]
works here
Eric Wieser (May 16 2023 at 22:51):
simp [(X)]
is a standard trick to fix simp [X]
not working
Kevin Buzzard (May 16 2023 at 22:51):
simp only [mul_add x]
also works, but I saw an example of the form forall x y z, x * (y + z) = ...
where you didn't have access to the variables.
Kevin Buzzard (May 16 2023 at 22:51):
Eric Wieser said:
simp only [(mul_add)]
works here
Whatever is going on?
Eric Wieser (May 16 2023 at 22:52):
mul_add
by itself is treated like @mul_add
I think. (mul_add)
is the same as @mul_add _ _ _
Eric Wieser (May 16 2023 at 22:52):
simp
treats it as a lemma name not an expression
Eric Wieser (May 16 2023 at 22:52):
Like #check mul_add
vs #check (mul_add)
Eric Wieser (May 16 2023 at 22:52):
So I think the cause of the failure is the same as whatever causes simp [@mul_add]
to fail
Eric Wieser (May 16 2023 at 22:53):
Not that I know what that is
Kevin Buzzard (May 16 2023 at 22:59):
I can't get the bracket trick to work on lines 1191-1193 of RingTheory.TensorProduct. The Lean 3 proof simp only [module_aux_apply, mul_smul, smul_comm a₁ b₂, algebra.tensor_product.tmul_mul_tmul, linear_map.mul_apply]
becomes
simp only [(· • ·), Algebra.TensorProduct.tmul_mul_tmul]
simp only [moduleAux_apply, mul_smul]
rw [smul_comm a₁ b₂]
in Lean 4 -- but regardless of what the Lean 3 proof is, is it surprising that this can't be compressed (by me) to one line in Lean 4? Note also that "you have to randomly put brackets around some of the proofs" is not particularly user-friendly.
Eric Wieser (May 16 2023 at 23:05):
Note also that "you have to randomly put brackets around some of the proofs" is not particularly user-friendly.
Especially since this makes @[simp]
useless, as the rule is also "anything tagged simp
that has this problem has to be added manually in ()
s"
Eric Wieser (May 16 2023 at 23:06):
Does replacing smul_comm a₁ b₂
with smul_comm a₁ b₂ _
work?
Eric Wieser (May 16 2023 at 23:06):
And if not, then smul_comm a₁ b₂ (_ : TheRightType)
will almost certainly work
Kyle Miller (May 16 2023 at 23:11):
If this is the "simp wants the canonical instance" problem, another thing is @mul_comm _ (_)
, where (_)
is at the instance argument, and the parentheses cause it to be treated like an implicit argument instead.
Kevin Buzzard (May 16 2023 at 23:12):
The goal is ⊢ a₂ • a₁ • b₂ • b₁ • m = a₂ • b₂ • a₁ • b₁ • m
on line 1193, and rw [smul_comm a₁ b₂]
works, but simp only [smul_comm a₁ b₂]
fails, simp only [smul_comm a₁ b₂ (b₁ • m)]
works, simp only [(smul_comm a₁ b₂)]
fails.
Eric Wieser (May 16 2023 at 23:16):
The paren trick only makes a difference for unapplied lemmas
Eric Wieser (May 16 2023 at 23:17):
Did simp only [smul_comm a₁ b₂ _]
fail? What type is m
?
Kyle Miller (May 16 2023 at 23:18):
This works on line 497 simp [@mul_add _ (_) (_) (_), @add_mul _ (_) (_) (_), h, h']
(though of course it's uglier than before)
Eric Wieser (May 16 2023 at 23:21):
Kyle, I thought we already found that simp[(mul_add), (add_mul), h, h']
works fine there too?
Eric Wieser (May 16 2023 at 23:21):
I think the two are equivalent
Kevin Buzzard (May 17 2023 at 00:46):
Eric Wieser said:
Did
simp only [smul_comm a₁ b₂ _]
fail? What type ism
?
simp only [smul_comm a₁ b₂ _]
fails. The relevant types are
R: Type ?u.2364979
A: Type ?u.2364982
B: Type ?u.2364985
M: Type ?u.2364988
inst✝¹¹: CommSemiring R
inst✝¹⁰: AddCommMonoid M
inst✝⁹: Module R M
inst✝⁸: Semiring A
inst✝⁷: Semiring B
inst✝⁶: Module A M
inst✝⁵: Module B M
inst✝⁴: Algebra R A
inst✝³: Algebra R B
inst✝²: IsScalarTower R A M
inst✝¹: IsScalarTower R B M
inst✝: SMulCommClass A B M
m: M
a₁: A
b₁: B
a₂: A
b₂: B
Eric Wieser (May 17 2023 at 01:05):
Then I predict simp only [smul_comm a₁ b₂ (_ : M)]
will work
Kevin Buzzard (May 17 2023 at 13:00):
Indeed it does work. But the bottom line is that this is very confusing for users. My mental model of simp
is "try rw [X]
for every X
which looks like it might work" and this is clearly not what's happening here, in the sense that rw
works but simp
needs some hints. Why is this happening? Is it worth opening an issue? Do you have any idea how to minimise? This is confusing for users.
Eric Wieser (May 17 2023 at 13:02):
My guess it that simp
gives up on the typeclass argument to smul_comm
rather than finding it by unification
Matthew Ballard (May 17 2023 at 13:03):
Is there a simp -vvvv
mode?
Eric Wieser (May 17 2023 at 13:03):
Which is a general pattern in Lean 4; there are many places where typeclass search is no longer allowed to fall back to unification. I think this was a deliberate choice to avoid confusion, but I can't help but feel this consistently makes things more confusing.
Kevin Buzzard (May 17 2023 at 13:05):
Matthew Ballard said:
Is there a
simp -vvvv
mode?
Do you just mean set_option trace.Meta.Tactic.simp true
or something else?
Kevin Buzzard (May 17 2023 at 13:05):
simp only [smul_comm a₁ b₂]
->
...
inst✝: SMulCommClass A B M
x y: A ⊗[R] B
m: M
a₁: A
b₁: B
a₂: A
b₂: B
⊢ a₂ • a₁ • b₂ • b₁ • m = a₂ • b₂ • a₁ • b₁ • m
Messages (1)
TensorProduct.lean:1195:6
[Meta.Tactic.simp.discharge] >> discharge?: SMulCommClass A B M
[Meta.Tactic.simp.discharge] smul_comm a₁ b₂, failed to discharge hypotheses
SMulCommClass A B M
Matthew Ballard (May 17 2023 at 13:06):
I confess to not knowing about that. Is it helpful?
Eric Wieser (May 17 2023 at 13:06):
Oh, I was expecting that to be SMulCommClass A B ?_
. I'm even more confused about why it's stuck there. Maybe it forgot to instantiate the metavariable before doing TC search?
Matthew Ballard (May 17 2023 at 13:10):
Thanks! That should be helpful to me though maybe not verbose enough in general
Kevin Buzzard (May 17 2023 at 13:11):
You can also switch on isDefEq
for more fun (I forget its full name, but set_option
is one of the few places where autocomplete currently works well in Lean 4, unlike imports and theorem names...)
Matthew Ballard (May 17 2023 at 13:12):
Yeah and tracing synthInstance
too
Matthew Ballard (May 17 2023 at 13:14):
I don’t know how many times I’ve had to break out a giant failingsimp
(that worked ML3) into line by line rw
because I can’t see inside
Sebastian Ullrich (May 17 2023 at 13:19):
Kevin Buzzard said:
(I forget its full name, but
set_option
is one of the few places where autocomplete currently works well in Lean 4, unlike imports and theorem names...)
What's this about theorem names?
Kevin Buzzard (May 17 2023 at 13:35):
Autocomplete: I'll start a new thread. I had assumed this was well-known. Maybe I'm doing something wrong
Kevin Buzzard (May 19 2023 at 12:45):
I tried to minimise this more. Is this behaviour expected?
import Mathlib.Algebra.Ring.Defs
variable {R : Type} [Semiring R]
def bar : AddZeroClass R := inferInstance -- note AddZeroClass -> Add -> HAdd
example (a b c : R) : a * (@HAdd.hAdd R R R (@instHAdd R (@AddZeroClass.toAdd R bar)) b c) = a * b + a * c := by
-- ⊢ a * (b + c) = a * b + a * c
simp only [mul_add] -- does nothing
rw [mul_add] -- works
done
I don't know whether randomly making def
s and throwing them into the typeclass system is asking for trouble. Note that in LinearAlgebra.TensorProduct
we have
-- porting note: This is added as a local instance for `SMul.aux`.
-- For some reason type-class inference in Lean 3 unfolded this definition.
def addMonoid : AddMonoid (M ⊗[R] N) :=
{ (addConGen (TensorProduct.Eqv R M N)).addMonoid with }
Eric Wieser (May 19 2023 at 12:50):
Probably that should have been reducible
Kevin Buzzard (May 19 2023 at 12:51):
That fixes the simple example
Kevin Buzzard (May 19 2023 at 13:02):
Rather annoyingly it doesn't fix the RingTheory.TensorProduct example (although mathlib compiles up to this point with the definition made reducible)
Kevin Buzzard (May 19 2023 at 14:46):
OK so maybe the reducible def is a red herring. This demonstrates the issue on master:
import Mathlib.RingTheory.TensorProduct
variable {R : Type} [CommSemiring R]
variable {A : Type} [Semiring A] [Algebra R A]
variable {B : Type} [Semiring B] [Algebra R B]
open TensorProduct -- for notation
example (x y z : A ⊗[R] B) : x * (y + z) = x * y + x * z := by
simp only [mul_add] -- works fine
done
-- stuff which comes up in the below trace
example : (AddZeroClass.toAdd : Add (A ⊗[R] B)) = (Distrib.toAdd : Add (A ⊗[R] B)) := rfl -- works fine
example : A ⊗[R] B = AddCon.Quotient (addConGen (TensorProduct.Eqv R A B)) := rfl -- works fine
example : (@HAdd.hAdd _ _ _ (@instHAdd _ (@AddZeroClass.toAdd _ (inferInstance))) : A → A → A)
= ((· + ·) : A → A → A) := rfl -- works fine
example (x y z : A ⊗[R] B) : x *
-- `(y + z)` but with `+` coming from `AddZeroClass` instead of via `Distrib`
(@HAdd.hAdd _ _ _ (@instHAdd _ (@AddZeroClass.toAdd _ (inferInstance))) y z)
= x * y + x * z := by
-- Goal is `x * (y + z) = x * y + x * z`
set_option trace.Meta.Tactic.simp true in
set_option trace.Meta.isDefEq true in
simp only [mul_add] -- does nothing
change x *
-- `(y + z)` but with `+` coming from `Distrib` (the default `+`)
(@HAdd.hAdd _ _ _ (@instHAdd _ (@Distrib.toAdd _ (inferInstance))) y z)
= x * y + x * z
simp only [mul_add] -- now works
done
The relevant trace is
...
[Meta.isDefEq] ❌ AddZeroClass.toAdd =?= Distrib.toAdd ▼
[] ❌ AddZeroClass.toAdd =?= Distrib.toAdd ▼
[] ❌ inferInstance.2 =?= NonUnitalNonAssocSemiring.toDistrib.2 ▼
[] ❌ AddSemigroup.toAdd =?= AddSemigroup.toAdd ▼
[] ❌ AddMonoid.toAddSemigroup.1 =?= AddMonoid.toAddSemigroup.1 ▼
[] ❌ AddSemigroup.toAdd =?= { add := fun x x_1 ↦ x + x_1 } ▼
[] ❌ (Function.Surjective.addSemigroup Quotient.mk'' (_ : Function.Surjective Quotient.mk'')
(_ :
∀ (x x_1 : FreeAddMonoid (A × B)),
Quotient.mk'' (x + x_1) = Quotient.mk'' (x + x_1))).1 =?= { add := fun x x_1 ↦ x + x_1 } ▼
[] ❌ { add := Add.add } =?= { add := fun x x_1 ↦ x + x_1 } ▼
[] ❌ Add.add =?= fun x x_1 ↦ x + x_1 ▼
[] ❌ fun x x_1 ↦ x + x_1 =?= fun a ↦ Add.add a ▼
[] ❌ A ⊗[R] B =?= AddCon.Quotient (addConGen (TensorProduct.Eqv R A B)) ▼
[] ❌ TensorProduct =?= @AddCon.Quotient
[onFailure] ❌ A ⊗[R] B =?= AddCon.Quotient (addConGen (TensorProduct.Eqv R A B))
[onFailure] ❌ A ⊗[R] B =?= AddCon.Quotient (addConGen (TensorProduct.Eqv R A B))
[onFailure] ❌ { add := Add.add } =?= { add := fun x x_1 ↦ x + x_1 }
[onFailure] ❌ { add := Add.add } =?= { add := fun x x_1 ↦ x + x_1 }
[Meta.Tactic.simp.discharge] @mul_add, failed to assign instance
Add (A ⊗[R] B)
sythesized value
Distrib.toAdd
is not definitionally equal to
AddZeroClass.toAdd
Kevin Buzzard (May 19 2023 at 14:47):
It looks to me like Meta.isDefEq
is telling lies with [onFailure] ❌ A ⊗[R] B =?= AddCon.Quotient (addConGen (TensorProduct.Eqv R A B))
. Any idea why?
def TensorProduct : Type _ :=
(addConGen (TensorProduct.Eqv R M N)).Quotient
Eric Wieser (May 19 2023 at 15:03):
Is it irreducible somehow?
Eric Wieser (May 19 2023 at 15:04):
And can you get to a #mwe that fails to prove the equality of those types by rfl
?
Kevin Buzzard (May 19 2023 at 16:32):
They're definitionally equal. The three examples near the top of the code are all rfl
. I wouldn't know where to start answering your question (or, in some sense, what it means)
Kevin Buzzard (May 19 2023 at 16:32):
Does [onFailure] ❌ A ⊗[R] B =?= AddCon.Quotient (addConGen (TensorProduct.Eqv R A B))
indicate that Lean can get into a state where this is not proved by rfl
?
Eric Wieser (May 19 2023 at 20:14):
I'm concerned there might be some unpleasant eta-and-quotients problem going on here
Eric Wieser (May 19 2023 at 20:14):
But maybe it just means "not proved by refl with reducible transparency"
Eric Wieser (May 19 2023 at 20:15):
Which is still a problem, because somehow we've ended up unfolded on the RHS but not the LHS and now lean says "oh, these aren't reducibly equal any more"
Eric Wieser (May 19 2023 at 20:27):
I think this is the failure:
import Mathlib.RingTheory.TensorProduct
variable {R : Type} [iR : CommSemiring R]
variable {A : Type} [iA : Semiring A] [iRA : Algebra R A]
variable {B : Type} [iB : Semiring B] [iRB : Algebra R B]
open TensorProduct -- for notation
example :
@Distrib.toAdd
(@TensorProduct R iR A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring A (@Semiring.toNonAssocSemiring A iA)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring B (@Semiring.toNonAssocSemiring B iB)))
(@Algebra.toModule R A iR iA iRA) (@Algebra.toModule R B iR iB iRB))
(@NonUnitalNonAssocSemiring.toDistrib
(@TensorProduct R iR A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring A (@Semiring.toNonAssocSemiring A iA)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring B (@Semiring.toNonAssocSemiring B iB)))
(@Algebra.toModule R A iR iA iRA) (@Algebra.toModule R B iR iB iRB))
(@NonAssocSemiring.toNonUnitalNonAssocSemiring
(@TensorProduct R iR A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring A (@Semiring.toNonAssocSemiring A iA)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring B (@Semiring.toNonAssocSemiring B iB)))
(@Algebra.toModule R A iR iA iRA) (@Algebra.toModule R B iR iB iRB))
(@Semiring.toNonAssocSemiring
(@TensorProduct R iR A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring A (@Semiring.toNonAssocSemiring A iA)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring B (@Semiring.toNonAssocSemiring B iB)))
(@Algebra.toModule R A iR iA iRA) (@Algebra.toModule R B iR iB iRB))
(@Algebra.TensorProduct.instSemiringTensorProductToAddCommMonoidToNonUnitalNonAssocSemiringToNonAssocSemiringToAddCommMonoidToNonUnitalNonAssocSemiringToNonAssocSemiringToModuleToModule
R iR A iA iRA B iB iRB))))
=
@AddZeroClass.toAdd
(@TensorProduct R iR A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring A (@Semiring.toNonAssocSemiring A iA)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring B (@Semiring.toNonAssocSemiring B iB)))
(@Algebra.toModule R A iR iA iRA) (@Algebra.toModule R B iR iB iRB))
(@inferInstance
(AddZeroClass
(@TensorProduct R iR A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring A (@Semiring.toNonAssocSemiring A iA)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring B (@Semiring.toNonAssocSemiring B iB)))
(@Algebra.toModule R A iR iA iRA) (@Algebra.toModule R B iR iB iRB)))
(@TensorProduct.addZeroClass R iR A B
(@NonUnitalNonAssocSemiring.toAddCommMonoid A
(@NonAssocSemiring.toNonUnitalNonAssocSemiring A (@Semiring.toNonAssocSemiring A iA)))
(@NonUnitalNonAssocSemiring.toAddCommMonoid B
(@NonAssocSemiring.toNonUnitalNonAssocSemiring B (@Semiring.toNonAssocSemiring B iB)))
(@Algebra.toModule R A iR iA iRA) (@Algebra.toModule R B iR iB iRB))) :=
by
with_reducible_and_instances
rfl
Eric Wieser (May 19 2023 at 20:29):
replacing docs4#inferInstance with id
fixes it!?
Eric Wieser (May 19 2023 at 20:31):
Also the name of docs4#Algebra.TensorProduct.instSemiringTensorProductToAddCommMonoidToNonUnitalNonAssocSemiringToNonAssocSemiringToAddCommMonoidToNonUnitalNonAssocSemiringToNonAssocSemiringToModuleToModule is outrageous
Kevin Buzzard (May 19 2023 at 20:34):
Maybe this is less painful to look at:
import Mathlib.RingTheory.TensorProduct
variable {R : Type} [CommSemiring R]
variable {A : Type} [Semiring A] [Algebra R A]
variable {B : Type} [Semiring B] [Algebra R B]
open TensorProduct -- for notation
example : (AddZeroClass.toAdd : Add (A ⊗[R] B)) = (Distrib.toAdd : Add (A ⊗[R] B)) := rfl
example : (AddZeroClass.toAdd : Add (A ⊗[R] B)) = (Distrib.toAdd : Add (A ⊗[R] B)) := by
with_reducible_and_instances
rfl -- fails
Kevin Buzzard (May 19 2023 at 20:36):
I hope you're laughing because this helps, as opposed to my having completely misunderstood something :-) I've never seen with_reducible_and_instances
before (as you can probably guess from my earlier posts). Is this example a problem? Is it a "diamond" if different instances aren't definitionally equal at this level of reducibility?
Eric Wieser (May 19 2023 at 20:38):
I'm laughing because mine was outrageously long
Eric Wieser (May 19 2023 at 20:39):
Yours doesn't look like quite the same state as mine (mine is exactly what simp
tried), but probably it makes sense to debug yours first
Kevin Buzzard (May 19 2023 at 20:40):
I'm surprised that mine isn't the same as yours, I've been looking at these terms for hours today and I thought that the Add
point was was precisely where they were diverging and it was all typeclass inference after that.
Kevin Buzzard (May 19 2023 at 20:42):
Note that this is yet another example from the port where simp
is failing but rw
ing the appropriate lemma saves the day.
Kevin Buzzard (May 19 2023 at 20:44):
(a) Are you expecting my example (or more generally our examples) to not fail and (b) could it be fixed by some simp!
tactic which tries harder?
Eric Wieser (May 19 2023 at 20:47):
Maybe https://github.com/leanprover-community/mathlib4/compare/remove-weird-TensorProduct-instance will help?
Eric Wieser (May 19 2023 at 20:47):
I think the problem is not simp
, but the rules for typeclass unification in the face of type wrappers
Eric Wieser (May 19 2023 at 20:48):
Maybe making TensorProduct
reducible would solve the problem!
Eric Wieser (May 19 2023 at 20:48):
(after all, it is "just" the quotient of a FreeAddMonoid; it's not like we put any incompatible instances on it)
Kevin Buzzard (May 19 2023 at 20:49):
I tried making TensorProduct
reducible and a simp call in the same file broke
Eric Wieser (May 19 2023 at 20:49):
That might be because of hacks that were added during porting
Kevin Buzzard (May 19 2023 at 20:49):
So you think that this is not a lean issue but a mathlib issue? In which case we could move to #mathlib4
Kevin Buzzard (May 19 2023 at 20:55):
Maybe typeclass unification in the face of type wrappers is still sufficiently leany
Gabriel Ebner (May 19 2023 at 21:00):
There's also the related (new?) gotcha that { add := fun (a b : A) => .. }
and { add := fun (a b : id A) => .. }
are no longer defeq instances.
Kevin Buzzard (May 19 2023 at 21:01):
So we should make id
reducible? ;-)
Gabriel Ebner (May 19 2023 at 21:02):
I actually wanted to write ᵒᵖ
, but that's been changed to a structure recently. But I think we have plenty of other def MyType := ..
.
Eric Wieser (May 19 2023 at 21:07):
Lex
might be a reasonable example
Eric Wieser (May 19 2023 at 21:08):
Gabriel Ebner said:
There's also the related (new?) gotcha that
{ add := fun (a b : A) => .. }
and{ add := fun (a b : id A) => .. }
are no longer defeq instances.
I think this is pretty much the same gotcha
Kevin Buzzard (May 19 2023 at 21:26):
Eric Wieser said:
That might be because of hacks that were added during porting
How do I see the diff between what mathlib3port thought and what Floris thought in !4#2539 ? After the squash merge has the relevant commit (the one generated by the script) vanished?
Eric Wieser (May 19 2023 at 21:27):
I'm sad we don't have a good answer to that
Kevin Buzzard (May 19 2023 at 21:27):
RingTheory.TensorProduct compiles on branch remove-weird-TensorProduct-instance
BTW
Eric Wieser (May 19 2023 at 21:27):
Your best options are:
- open up the porting PR and look at the original commits there
- download the latest version from mathport, and pretend the hundreds of lines of warnings aren't there
Eric Wieser (May 19 2023 at 21:28):
The first option doesn't capture any additional hacks that were added in further porting PRs
Kevin Buzzard (May 19 2023 at 21:29):
example : (AddZeroClass.toAdd : Add (A ⊗[R] B)) = (Distrib.toAdd : Add (A ⊗[R] B)) := by
with_reducible_and_instances
rfl -- fails
still fails on remove-weird-TensorProduct-instance
Eric Wieser (May 19 2023 at 21:31):
Thanks for checking, I didn't bother doing a build locally, and of course was punished by having to wait for the entire build to get oleans
Eric Wieser (May 19 2023 at 21:32):
I'm not really surprised, but it was worth a try
Eric Wieser (May 19 2023 at 21:32):
I think diagnosing the failure that made us need that instance in the first place might help work out what's going on here
Kevin Buzzard (May 19 2023 at 22:01):
I tried making TensorProduct
reducible but it's horrible:
synthesized type class instance is not definitionally equal to expression inferred by typing rules, synthesized
AddCon.addMonoid (addConGen (Eqv R M N))
inferred
AddCommMonoid.toAddMonoid
etc (200 errors)
Kevin Buzzard (May 19 2023 at 22:03):
(on your branch,
nsmul_succ := by
simp [Nat.succ_eq_one_add, TensorProduct.one_smul, TensorProduct.add_smul, add_comm]
fixes the first one, and then all hell breaks loose)
Kevin Buzzard (May 19 2023 at 22:06):
(Re the diff, I just downloaded the raw files after each relevant commit from GH and then compared locally)
Kevin Buzzard (May 19 2023 at 22:20):
So I'm now out of ideas and out of my depth with this one. Note that this works:
example : (AddZeroClass.toAdd : Add (R)) = (Distrib.toAdd : Add (R)) := by
with_reducible_and_instances
rfl -- works
I have no idea what this with_reducible_and_instances
thing is about, or how to debug further.
Kevin Buzzard (May 19 2023 at 23:02):
OK, next example (found by Jeremy Tan in porting PR !4#4120):
import Mathlib.Topology.MetricSpace.Isometry
variable {PF : Type _} {PE : Type _} [MetricSpace PF] [MetricSpace PE]
example {f : PF → PE} (hi : Isometry f) (p : PF) : dist (f p) (f p) = 37 := by
simp only [hi.dist_eq] -- no change
rw [hi.dist_eq] -- dist (f p) (f p) -> dist p p
change dist p p = 37
sorry
Kevin Buzzard (May 19 2023 at 23:03):
simp only [hi.dist_eq p p]
doesn't work, simp only [(hi.dist_eq)]
doesn't work
Kevin Buzzard (May 19 2023 at 23:05):
Solved: this one is fixed by changing {PF : Type _}
to {PF : Type u}
. OK so what the heck is going on there?
Kevin Buzzard (May 19 2023 at 23:06):
This is the second time today that a problem has been solved by changing Type _
to Type u
. Is Type _
considered harmful?
Kevin Buzzard (May 19 2023 at 23:07):
Kevin Buzzard (May 19 2023 at 23:31):
OK next one:
import Mathlib.CategoryTheory.Filtered
universe v
open CategoryTheory CategoryTheory.Category
namespace CategoryTheory.Limits
variable {J K : Type v} [SmallCategory J] [SmallCategory K]
variable (F : J × K ⥤ Type v)
variable [IsFiltered K]
variable [FinCategory J]
example (j : J) (k: J → K) (k' k'' : K) (g: (j : J) → k j ⟶ k') (kf: {j j' : J} → (j ⟶ j') → K)
(gf: {j j' : J} → (f : j ⟶ j') → k' ⟶ kf f) (i: {j j' : J} → (f : j ⟶ j') → kf f ⟶ k'')
(y: (j : J) → F.obj (j, k j)) :
F.map ((𝟙 j, 𝟙 k'') : (j, k'') ⟶ (j, k''))
(F.map ((𝟙 j, g j ≫ gf (𝟙 j) ≫ i (𝟙 j)) : (j, k j) ⟶ (j, _)) (y j)) =
F.map ((𝟙 j, g j ≫ gf (𝟙 j) ≫ i (𝟙 j)) : (j, k j) ⟶ (j, _)) (y j) := by
simp only [Bifunctor.map_id_comp] -- does nothing
rw [Bifunctor.map_id_comp] -- does something
rw [Bifunctor.map_id_comp] -- does something
sorry
No Type _
in sight, the bracket trick doesn't fix it, it's unfortunately now my bedtime. This came from !4#3605 (now merged).
Kevin Buzzard (May 19 2023 at 23:38):
One might hope that this one is minimisable to something mathlib-free without too much trouble.
Eric Wieser (May 20 2023 at 08:18):
Kevin Buzzard said:
Is
Type _
considered harmful?
I'd say yes. It's certainly far more harmful than it was in lean 3.
The lean 4
variable {A : Type _}
def something : that_uses A := sorry
is the same as the Lean 3
def something {A : Type _} : that_uses A := sorry
But not the same as using variable {A : Type _}
in Lean 3
Yaël Dillies (May 20 2023 at 08:23):
Should we repurpose Type*
to mean variable {A : Type*}
?
Eric Wieser (May 20 2023 at 08:27):
I would like it to mean universe uA
{A : Type uA}
Yaël Dillies (May 20 2023 at 08:43):
Yeah sorry that's I meant
Ruben Van de Velde (May 20 2023 at 09:13):
If Type _ is a footgun, wouldn't it be better to suggest changing it?
Yaël Dillies (May 20 2023 at 09:13):
You mean, in core Lean?
Eric Wieser (May 20 2023 at 09:20):
It's not so much that Type _
is a new footgun, but that variable
is much better at aiming things towards your feet than it used to be
Kevin Buzzard (May 20 2023 at 11:44):
Oh docs4#CategoryTheory.Bifunctor.map_id_comp just doesn't work at all with simp
; here's a much simpler example (just copying the definition of the declaration):
import Mathlib.CategoryTheory.Products.Basic
open CategoryTheory
universe v₁ v₂ v₃ u₁ u₂ u₃
variable {C : Type u₁} {D : Type u₂} {E : Type u₃}
variable [Category.{v₁} C] [Category.{v₂} D] [Category.{v₃} E]
@[simp]
theorem map_id_comp (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
rw [← Functor.map_comp, prod_comp, Category.comp_id]
example (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
simp -- does nothing
simp only [map_id_comp] -- does nothing
rw [map_id_comp] -- closes goal
done
Kevin Buzzard (May 20 2023 at 12:33):
Now mathlib-free:
universe v₁ v₂ v u₁ u₂ u
class Category (V : Type u) where
Hom : V → V → Type v
id : ∀ X : V, Hom X X
comp : ∀ {X Y Z : V}, (Hom X Y) → (Hom Y Z) → (Hom X Z)
infixr:10 " ⟶ " => Category.Hom -- \hom
notation "𝟙" => Category.id -- \b1
infixr:80 " ≫ " => Category.comp -- \gg
variable {C : Type u₁} {D : Type u₂} [Category.{v₁} C] [Category.{v₂} D]
instance prod : Category.{max v₁ v₂} (C × D) where
Hom X Y := (X.1 ⟶ Y.1) × (X.2 ⟶ Y.2)
id X := ⟨𝟙 X.1, 𝟙 X.2⟩
comp f g := (f.1 ≫ g.1, f.2 ≫ g.2)
variable [Category.{v₁} C] [Category.{v₂} D] [Category.{v₃} E]
structure Functor' (V : Type u₁) [Category.{v₁} V] (W : Type u₂) [Category.{v₂} W] where
obj : V → W
map : ∀ {X Y : V}, (X ⟶ Y) → (obj X ⟶ obj Y)
infixr:26 " ⥤ " => Functor' -- \func
@[simp]
theorem map_id_comp (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
sorry
example (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
simp -- does nothing
simp only [map_id_comp] -- does nothing
simp only [(map_id_comp)] -- does nothing
simp only [map_id_comp F W f g] -- does nothing
rw [map_id_comp] -- closes goal
done
What's going on here?
Kevin Buzzard (May 20 2023 at 12:36):
So in summary, I'm baffled by: (1) the above example (mathlib-free), (2) this example (using mathlib) where changing Type _
to Type u
makes simp
magically work, and (3) the discussion about tensor products above, where I am unclear about whether that is something which needs to be fixed in mathlib and, if so, how to fix it. They're all examples from the port where simp [X]
failed but rw [X]
worked.
Kevin Buzzard (May 20 2023 at 12:38):
@Mario Carneiro any ideas? I've tried to summarise this thread in the above post.
Mario Carneiro (May 20 2023 at 14:34):
The MWE example fails because map_id_comp F W f g
has the type
Functor'.map F (@Prod.mk ((W, X).fst ⟶ (W, Z).fst) ((W, X).snd ⟶ (W, Z).snd) (𝟙 W) (f ≫ g)) = ...
and the goal has type
Functor'.map F (@Prod.mk (W ⟶ W) (X ⟶ Z) (𝟙 W) (f ≫ g)) = ...
This works:
example (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
have : F.map (X := (W, X)) (Y := (W, Z)) (@Prod.mk (W ⟶ W) (X ⟶ Z) (𝟙 W) (f ≫ g)) = _ :=
map_id_comp F W f g
simp only [this]
Kevin Buzzard (May 20 2023 at 14:39):
It worked in Lean 3. Do you understand why?
Mario Carneiro (May 20 2023 at 14:39):
My guess is that the (W, X).fst
subterm is entered into the discrimination tree without being reduced, which means that it will not match W
Mario Carneiro (May 20 2023 at 14:39):
In this case you can fix the issue at the simp lemma
Mario Carneiro (May 20 2023 at 14:40):
this also works
@[simp]
theorem map_id_comp (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map (X := (W, X)) (Y := (W, Z)) (@Prod.mk (W ⟶ W) (X ⟶ Z) (𝟙 W) (f ≫ g)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
sorry
example (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
simp only [map_id_comp]
Mario Carneiro (May 20 2023 at 14:40):
this is a bug, I think
Kevin Buzzard (May 20 2023 at 15:00):
Is there some way that an end user like me can see this forest of discrimination trees somehow?
Mario Carneiro (May 20 2023 at 15:04):
I'm using this for testing right now
open Lean Meta Elab Term
elab "discriminate" t:term : tactic => do
let e ← elabTerm t none
logInfo <| repr (← @DiscrTree.mkPath true e)
@[simp]
theorem map_id_comp (F : C × D ⥤ E) (W : C) {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z)) =
F.map ((𝟙 W, f) : (W, X) ⟶ (W, Y)) ≫ F.map ((𝟙 W, g) : (W, Y) ⟶ (W, Z)) := by
discriminate Functor'.map F ((𝟙 W, f ≫ g) : (W, X) ⟶ (W, Z))
discriminate F.map (X := (W, X)) (Y := (W, Z)) (@Prod.mk (W ⟶ W) (X ⟶ Z) (𝟙 W) (f ≫ g))
Mario Carneiro (May 20 2023 at 15:07):
Here's an even simpler version:
discriminate (W, X).fst
discriminate W
#[Lean.Meta.DiscrTree.Key.proj `Prod 0 0, Lean.Meta.DiscrTree.Key.const `Prod.mk 4,
Lean.Meta.DiscrTree.Key.fvar (Lean.Name.mkNum `_uniq 32614) 0,
Lean.Meta.DiscrTree.Key.fvar (Lean.Name.mkNum `_uniq 32616) 0,
Lean.Meta.DiscrTree.Key.fvar (Lean.Name.mkNum `_uniq 32983) 0,
Lean.Meta.DiscrTree.Key.fvar (Lean.Name.mkNum `_uniq 32984) 0]
#[Lean.Meta.DiscrTree.Key.fvar (Lean.Name.mkNum `_uniq 32983) 0]
Mario Carneiro (May 20 2023 at 15:07):
As I understand it this should be reduced
Mario Carneiro (May 20 2023 at 15:07):
so that they are the same
Mario Carneiro (May 20 2023 at 15:16):
It appears this is deliberate, this is the action of the simpleReduce := true
flag in DiscrTree, which was added in response to a previous mathlib bug report
Mario Carneiro (May 20 2023 at 15:17):
basically without it you can't have simp lemmas like (W, X).fst = W
because they look like W = W
to simp
Adam Topaz (May 20 2023 at 16:17):
So do we just have to add a ton of little simp lemmas like (W,X).fst = W
?
Mario Carneiro (May 20 2023 at 16:23):
I think this requires some more design work, because we can't just undo the previous bugfix, there is a tension here
Mario Carneiro (May 20 2023 at 16:23):
like, do you want that to be a simp lemma or not?
Mario Carneiro (May 20 2023 at 16:23):
should simp see through it or not?
Mario Carneiro (May 20 2023 at 16:25):
the example that is used in the DiscrTree
docstring is
@[simp] theorem liftOn_mk (a : α) (f : α → γ) (h : ∀ a₁ a₂, r a₁ a₂ → f a₁ = f a₂) :
Quot.liftOn (Quot.mk r a) f h = f a := rfl
(which is a real simp lemma in mathlib)
Adam Topaz (May 20 2023 at 16:32):
If we had a separate tactic that does all such reductions, the we wouldn’t need any additional silly simp lemmas, and for the category theory library we could just add that tactic to aesop_cat
’s rules
Adam Topaz (May 20 2023 at 16:35):
In lean3 it was quite common to use dsimp only
for such a purpose
Mario Carneiro (May 20 2023 at 16:39):
I think it might make sense to let simp apply lemmas up to reducible defeq as normal (meaning that liftOn_mk
et al would loop and are not marked simp
), and then when it is done to postprocess any remaining result by doing reducible defeq reductions (i.e. dsimp only
)
Mario Carneiro (May 20 2023 at 16:40):
without that follow up step something like Quot.liftOn (Quot.mk r a) f h = sorry
would be left unreduced because simp would look at it and see f a
and hence have nothing to do
Adam Topaz (May 20 2023 at 16:47):
BTW I recently ran into an example where rw
doesn’t work (without explicitly filling in some parameters) but simp only …
does. It didn’t involve any binders, and rw
really should have worked. It came up in !4#4113 . I didn’t think it was a big deal, but if it is, I could try to minimize the example.
Adam Topaz (May 20 2023 at 16:48):
Specifically it was the simp only [<- types_comp_apply]
steps in that PR
Mario Carneiro (May 20 2023 at 16:56):
Hard to say, it doesn't sound familiar
Adam Topaz (May 20 2023 at 17:06):
I thought it might be related, because I suspected some differences in reduction strategies were to blame here as well.
Adam Topaz (May 20 2023 at 17:07):
But I didn’t really spend too much time investigating.
Oliver Nash (Aug 22 2023 at 09:38):
I'm quite surprised by a failure illustrated in Example 2
below. It has the characteristics that:
simp only [Pi.smul_apply]
fails but bothsimp only [Pi.smul_apply _]
andrw [Pi.smul_apply]
work,- the failure is triggered by adding
let
statement to the context which does not change the goal.
Here are the examples:
import Mathlib.Data.Pi.Algebra
import Mathlib.Algebra.Algebra.Basic
-- Example 1: succeeds
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
simp only [Pi.smul_apply] -- succeeds
-- Example 2: fails!
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let bar : SMul R R := SMulZeroClass.toSMul
simp only [Pi.smul_apply] -- Fails!
Oliver Nash (Aug 22 2023 at 09:38):
Further experimentation reveals other things like:
-- Example 3: succeeds
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let foo : SMul R R := Algebra.toSMul
simp only [Pi.smul_apply] -- succeeds
-- Example 4: succeeds
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let bar : SMul R R := SMulZeroClass.toSMul
let foo : SMul R R := Algebra.toSMul
simp only [Pi.smul_apply] -- succeeds
-- Example 5: succeeds
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let foo : SMul R R := Algebra.toSMul
let bar : SMul R R := SMulZeroClass.toSMul
have foobar : foo = bar := rfl
simp only [foobar, Pi.smul_apply] -- succeeds
Oliver Nash (Aug 22 2023 at 09:39):
(This came up in the course of trying to understand why I had to write Pi.smul_apply _
instead of just Pi.smul_apply
in #6666 )
Matthew Ballard (Aug 22 2023 at 09:42):
If you bump maxHeartbeats.synthInstance
does Example 2 succeed?
Oliver Nash (Aug 22 2023 at 09:43):
Nope. I just tried adding: set_option synthInstance.maxHeartbeats 10000000
and the failure persists (everything is instant btw.)
Matthew Ballard (Aug 22 2023 at 09:56):
What does trace.Meta.synthInstance
look like between the Example 1 and 2? (Sorry, I would look but am not at a computer.)
Oliver Nash (Aug 22 2023 at 09:58):
If I place set_option trace.Meta.synthInstance true
at the top of the file and then place the cursor over the simp only
lines, then for both Example 1
and Example 2
I see the same result, namely:
[Meta.synthInstance] ✅ α → SMul R R ▶
Matthew Ballard (Aug 22 2023 at 10:23):
I should asked for isDefEq
! Here is the failure
[Meta.isDefEq] [0.000443s] ❌ fun i ↦ Algebra.toSMul =?= fun i ↦ bar ▼
[] [0.000000s] ✅ α =?= α
[] [0.000439s] ❌ Algebra.toSMul =?= bar ▼
[] [0.000431s] ❌ Algebra.toSMul =?= SMulZeroClass.toSMul ▼
[] [0.000366s] ❌ (Algebra.id R).1 =?= SMulWithZero.toSMulZeroClass.1 ▼
[] [0.000293s] ❌ (Algebra.id R).1 =?= { smul := fun x x_1 ↦ x * x_1 } ▼
[] [0.000000s] ✅ SMul R R =?= SMul R R
[] [0.000258s] ❌ SMul.smul =?= fun x x_1 ↦ x * x_1 ▼
[] [0.000252s] ❌ fun x x_1 ↦ x * x_1 =?= fun a ↦ SMul.smul a ▼
[] [0.000000s] ✅ R =?= R
[] [0.000249s] ❌ fun x ↦ x✝ * x =?= SMul.smul x✝ ▼
[] [0.000234s] ❌ fun x ↦ x✝ * x =?= fun a ↦ SMul.smul x✝ a ▼
[] [0.000000s] ✅ R =?= R
[] [0.000230s] ❌ x✝¹ * x✝ =?= SMul.smul x✝¹ x✝ ▼
[] [0.000023s] ❌ @HMul.hMul =?= @SMul.smul
[onFailure] [0.000004s] ❌ x✝¹ * x✝ =?= SMul.smul x✝¹ x✝
[] [0.000110s] ❌ Mul.mul x✝¹ x✝ =?= SMul.smul x✝¹ x✝ ▼
[] [0.000012s] ❌ @Mul.mul =?= @SMul.smul
[onFailure] [0.000003s] ❌ Mul.mul x✝¹ x✝ =?= SMul.smul x✝¹ x✝
[onFailure] [0.000003s] ❌ Mul.mul x✝¹ x✝ =?= SMul.smul x✝¹ x✝
[onFailure] [0.000006s] ❌ (Algebra.id R).1 =?= { smul := fun x x_1 ↦ x * x_1 }
vs
[Meta.isDefEq] [0.000002s] ✅ fun i ↦ Algebra.toSMul =?= fun i ↦ Algebra.toSMul
Matthew Ballard (Aug 22 2023 at 10:32):
Note that changing CommRing
to Ring
makes things succeed.
Matthew Ballard (Aug 22 2023 at 10:33):
Going through Algebra
does seem to happen quite often with CommRing
. Is this intended?
Oliver Nash (Aug 22 2023 at 10:34):
I don't know (and thanks for digging into this btw!).
Oliver Nash (Aug 22 2023 at 10:36):
I don't understand why adding something to the context using let
can have such an (or indeed any) effect and I wonder should this be the case.
In fact my understanding here is all very shallow, what I really want to know is: can we make simp
work in cases like this?
Matthew Ballard (Aug 22 2023 at 10:50):
This is odd. Replacing simp only
with rw
in Example 2 gives unification success
[Meta.isDefEq] [0.001067s] ✅ fun i ↦ Algebra.toSMul =?= fun i ↦ bar ▼
[] [0.000000s] ✅ α =?= α
[] [0.001064s] ✅ Algebra.toSMul =?= bar ▼
[] [0.001052s] ✅ Algebra.toSMul =?= SMulZeroClass.toSMul ▼
[] [0.000927s] ✅ (Algebra.id R).1 =?= SMulWithZero.toSMulZeroClass.1 ▼
[] [0.000843s] ✅ { smul := fun c x ↦ ↑(RingHom.id R) c * x } =?= { smul := fun x x_1 ↦ x * x_1 } ▼
[] [0.000824s] ✅ fun c x ↦ ↑(RingHom.id R) c * x =?= fun x x_1 ↦ x * x_1 ▶
[] [0.000000s] ✅ R =?= R
[] [0.000000s] ✅ R =?= R
Oliver Nash (Aug 22 2023 at 10:50):
That's right, I even highlighted this in my first post!
Oliver Nash (Aug 22 2023 at 10:51):
Even weirder (I think) is that simp only [Pi.smul_apply _]
works whereas simp only [Pi.smul_apply]
fails!
Damiano Testa (Aug 22 2023 at 10:56):
Oliver Nash said:
I don't understand why adding something to the context using
let
can have such an (or indeed any) effect and I wonder should this be the case.In fact my understanding here is all very shallow, what I really want to know is: can we make
simp
work in cases like this?
I've seen this already and in the past it was that the let
introduces some mdata
in the target expression. I do not exactly know why, but I suspect that this is some of the issue.
Damiano Testa (Aug 22 2023 at 10:57):
Here is how you can see it:
open Lean Meta Elab Tactic in
-- Example 2: fails!
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
run_tac do -- app
logInfo (← getMainTarget).ctorName
let bar : SMul R R := SMulZeroClass.toSMul
run_tac do -- mdata
logInfo (← getMainTarget).ctorName
simp only [Pi.smul_apply] -- Fails!
Damiano Testa (Aug 22 2023 at 10:59):
So, the let
binding changes the expression underlying your goal. .mdata
is DefEq
to the original thing, but there might be a missing consumeMData
somewhere.
Damiano Testa (Aug 22 2023 at 11:02):
This is how the two expressions begin:
-- pre let: inspect '(r • f) a = r • f a'
'Eq' -- app
|-'_uniq.1612' -- fvar
|-'HSMul.hSMul' -- app
| |-'_uniq.1612' -- fvar
| |-('a._@.Mathlib.Tactic.LeanUtils.to_left_right._hyg.47') -- forallE
-- post let: inspect '(r • f) a = r • f a'
'[noImplicitLambda := true] (r • f) a = r • f a' -- mdata
|-'Eq' -- app
| |-'_uniq.1612' -- fvar
| |-'HSMul.hSMul' -- app
| | |-'_uniq.1612' -- fvar
| | |-('a._@.Mathlib.Tactic.LeanUtils.to_left_right._hyg.47') -- forallE
Damiano Testa (Aug 22 2023 at 11:03):
The extra mdata field is [noImplicitLambda := true]
, while (r • f) a = r • f a
is the "initial" expression.
Damiano Testa (Aug 22 2023 at 11:13):
Matthew, maybe this is entirely unrelated, but I have seen you fight very successfully a great battle with the let
s in instances. I wonder now if the mdata
could be responsible for some of the issues...
Damiano Testa (Aug 22 2023 at 11:14):
As far as I know, mdata
is a new feature of Lean 4 Expr
: Lean 3 expr
did not have an mdata
field.
Jon Eugster (Aug 22 2023 at 11:21):
I haven't read most of the details in this topic, but this bug was also observed in the last nightly bump #6019 (see comment and the comment below that) where sometimes simp_rw
wouldn't work without these extra _
. (simp_rw
is implemented as simp only
-calls)
Oliver Nash (Aug 22 2023 at 12:30):
Interesting. I think having to add these extra underscores is very bad because it means the user cannot trust simp
to find the relevant lemmas and now needs to learn API by hand.
Oliver Nash (Aug 22 2023 at 12:32):
Interesting. I think having to add these extra underscores is very bad because it means the user cannot trust simp
to find the relevant lemmas and now needs to learn API by hand.
Matthew Ballard (Aug 22 2023 at 12:38):
Damiano Testa said:
Matthew, maybe this is entirely unrelated, but I have seen you fight very successfully a great battle with the
let
s in instances. I wonder now if themdata
could be responsible for some of the issues...
Good idea. Though most of the problems I’ve seen, that we thought were due to expanded let
’s, were our old friend structure eta, there are still some slowdowns in AG I cannot place. I’ll keep an eye peeled
Damiano Testa (Aug 22 2023 at 12:42):
Anyway, there is something else at play, since even manually replacing the target expression with the cleared mdata
, leaves simp
unable to continue:
open Lean Meta Elab Tactic in
-- Example 2: fails!
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
run_tac do -- app
logInfo (← getMainTarget).ctorName
let bar : SMul R R := SMulZeroClass.toSMul
run_tac do -- before: mdata
-- after: app
let tgt := ← getMainTarget
let mtgt := tgt.consumeMData
logInfo m!"before: {tgt.ctorName}\nafter: {mtgt.ctorName}"
let new := ← (← getMainGoal).replaceTargetDefEq mtgt
setGoals [new]
simp only [Pi.smul_apply] -- Fails!
Matthew Ballard (Aug 22 2023 at 12:43):
Also Ring
would suffer similar to CommRing
I imagine
Damiano Testa (Aug 22 2023 at 12:51):
By the way, any underscore seems to work:
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let bar : SMul R R := SMulZeroClass.toSMul
simp only [Pi.smul_apply (I := _)] -- Success!
Matthew Ballard (Aug 22 2023 at 13:51):
Here is where things go off track: we are trying
(Algebra.id R).1 =?= SMulWithZero.toSMulZeroClass.1
which unfolds to
(Algebra.id R).1 =?= { smul := fun x x_1 ↦ x * x_1 }
and then to
SMul.smul =?= fun x x_1 ↦ x * x_1
when it fails and
{ smul := fun c x ↦ ↑(RingHom.id R) c * x } =?= { smul := fun x x_1 ↦ x * x_1 }
when it succeeds with rw
Matthew Ballard (Aug 22 2023 at 13:54):
That SMul.smul
is @SMul.smul R R (Algebra.id R).1
Matthew Ballard (Aug 22 2023 at 14:17):
This works
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let foo := (Algebra.id R).toSMul
let bar : SMul R R := SMulZeroClass.toSMul
have : foo = bar := rfl
simp only [Pi.smul_apply, this]
and this doesn't
example {α R : Type*} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let foo := (Algebra.id R).toSMul
let bar : SMul R R := SMulZeroClass.toSMul
have : foo = bar := rfl
simp only [Pi.smul_apply]
Matthew Ballard (Aug 22 2023 at 14:18):
In the rfl
proof, we again unfold to
{ smul := fun c x ↦ ↑(RingHom.id R) c * x } =?= { smul := fun x x_1 ↦ x * x_1 }
Matthew Ballard (Aug 22 2023 at 14:29):
It will just never unfold @SMul.smul R R (Algebra.id R).1
inside the simp
Oliver Nash (Aug 22 2023 at 14:31):
I wish I had something that I could add; all I can say is that I'm very grateful that this is still receiving attention.
Matthew Ballard (Aug 22 2023 at 14:37):
With
simp only [Pi.smul_apply (I := _)]
it creates a metavariable for the pi instance of SMul
instead of using bar
and assigns it during unification
[Meta.isDefEq] (?b • ?x) ?i =?= (r • f) a
Damiano Testa (Aug 22 2023 at 14:44):
I am not really talking about anything that I know, but does this look like lean should be waiting a little longer for something to unify?
Matthew Ballard (Aug 22 2023 at 14:49):
Hmm. Actually it does the same thing (creates a metavariable and assigns it) when it fails but then looks again for a pi instance.
Matthew Ballard (Aug 22 2023 at 14:51):
It looks for the pi SMul instance when it succeeds but it fails to find it after a few attempts.
Matthew Ballard (Aug 22 2023 at 14:51):
And just carries on to completion
Matthew Ballard (Aug 22 2023 at 14:52):
These traces are not linear right?
Kevin Buzzard (Aug 22 2023 at 14:58):
What do you mean? The indentation is telling you the tree I guess.
Matthew Ballard (Aug 22 2023 at 14:59):
I get a bunch of separate nodes in the infoview window before expanding. Is this the order that Lean is doing things during its program flow?
Kevin Buzzard (Aug 22 2023 at 15:00):
I guess I was assuming that but I guess I can't be sure.
Kevin Buzzard (Aug 22 2023 at 15:49):
darn it, I minimised to make it mathlib free (this was easy thanks to https://github.com/kbuzzard/MweSkeletons) but then the problem goes away :-/
Kevin Buzzard (Aug 22 2023 at 17:09):
Oh I got it working on the tube home, will post in a bit
Kevin Buzzard (Aug 22 2023 at 17:30):
So here is a 500-line repro of the issue with no imports. I'm sure it can be cut down much further: I made it by cutting and pasting from other MWEs I've made over the last year or so.
Eric Wieser (Aug 22 2023 at 17:32):
I don't see it mentioned above, but simp only [(Pi.smul_apply)]
works instead of simp only [Pi.smul_apply _]
Eric Wieser (Aug 22 2023 at 17:32):
This came up repeatedly during the port
Eric Wieser (Aug 22 2023 at 17:32):
The problem is that simp only [foo]
actually means simp only [@foo]
, and if you actually want to use the expression foo
as is, it needs to be (foo)
to stop simp
trying to be clever.
Kevin Buzzard (Aug 22 2023 at 17:48):
But does this explain the regression when you randomly add a term to the context?
Eric Wieser (Aug 22 2023 at 18:16):
No, it just explains why adding a seemingly obvious underscore makes a difference, because actually the change is that you added ~5 underscores, some of them as typeclass arguments
Kevin Buzzard (Aug 22 2023 at 18:37):
I'm working on minimising the repro BTW
Kevin Buzzard (Aug 22 2023 at 18:53):
universe u v w
section algebra_hierarchy_classes_to_comm_ring
class Zero (α : Type u) where
zero : α
class One (α : Type u) where
one : α
class Semiring (α : Type u) extends Add α, Mul α, Zero α, One α
class CommSemiring (R : Type u) extends Semiring R
class Ring (R : Type u) extends Semiring R
class CommRing (α : Type u) extends Ring α
end algebra_hierarchy_classes_to_comm_ring
section algebra_hierarchy_morphisms
class FunLike (F : Sort _) (α : outParam (Sort _)) (β : outParam <| α → Sort _) where
coe : F → ∀ a : α, β a
instance (priority := 100) FunLike.insthasCoeToFun {F α : Sort _} {β : α → Sort _} [FunLike F α β] : CoeFun F fun _ ↦ ∀ a : α, β a where coe := FunLike.coe
structure ZeroHom (M : Type _) (N : Type _) [Zero M] [Zero N] where
toFun : M → N
class ZeroHomClass (F : Type _) (M N : outParam (Type _)) [Zero M] [Zero N]
extends FunLike F M fun _ => N where
structure OneHom (M : Type _) (N : Type _) [One M] [One N] where
toFun : M → N
class OneHomClass (F : Type _) (M N : outParam (Type _)) [One M] [One N]
extends FunLike F M fun _ => N where
structure AddHom (M : Type _) (N : Type _) [Add M] [Add N] where
toFun : M → N
class AddHomClass (F : Type _) (M N : outParam (Type _)) [Add M] [Add N]
extends FunLike F M fun _ => N where
structure MulHom (M : Type _) (N : Type _) [Mul M] [Mul N] where
toFun : M → N
infixr:25 " →ₙ* " => MulHom
class MulHomClass (F : Type _) (M N : outParam (Type _)) [Mul M] [Mul N]
extends FunLike F M fun _ => N where
structure AddMonoidHom (M : Type _) (N : Type _) [Add M] [Zero M] [Add N] [Zero N] extends
ZeroHom M N, AddHom M N
infixr:25 " →+ " => AddMonoidHom
class AddMonoidHomClass (F : Type _) (M N : outParam (Type _)) [Add M] [Zero M] [Add N] [Zero N]
extends AddHomClass F M N, ZeroHomClass F M N
structure MonoidHom (M : Type _) (N : Type _) [Mul M] [One M] [Mul N] [One N] extends
OneHom M N, M →ₙ* N
infixr:25 " →* " => MonoidHom
class MonoidHomClass (F : Type _) (M N : outParam (Type _)) [Mul M] [One M] [Mul N] [One N]
extends MulHomClass F M N, OneHomClass F M N
structure MonoidWithZeroHom (M : Type _) (N : Type _)
[Mul M] [Zero M] [One M] [Mul N] [Zero N] [One N] extends ZeroHom M N, MonoidHom M N
infixr:25 " →*₀ " => MonoidWithZeroHom
structure NonUnitalRingHom (α β : Type _) [Add α] [Zero α] [Mul α]
[Add β] [Zero β] [Mul β] extends α →ₙ* β, α →+ β
class MonoidWithZeroHomClass (F : Type _) (M N : outParam (Type _)) [Mul M] [Zero M] [One M]
[Mul N] [Zero N] [One N] extends MonoidHomClass F M N, ZeroHomClass F M N
infixr:25 " →ₙ+* " => NonUnitalRingHom
structure RingHom (α : Type _) (β : Type _) [Semiring α] [Semiring β] extends
α →* β, α →+ β, α →ₙ+* β, α →*₀ β
infixr:25 " →+* " => RingHom
class RingHomClass (F : Type _) (α β : outParam (Type _)) [Semiring α]
[Semiring β] extends MonoidHomClass F α β, AddMonoidHomClass F α β,
MonoidWithZeroHomClass F α β
instance instRingHomClass {R S : Type _} {_ : Semiring R} {_ : Semiring S} :
RingHomClass (R →+* S) R S where
coe f := f.toFun
-- this is needed to create the troublesome instance `Algebra.instid`
def RingHom.id (α : Type _) [Semiring α] : α →+* α := by
refine' { toFun := _root_.id.. }
end algebra_hierarchy_morphisms
section HSMul_stuff
class HSMul (α : Type u) (β : Type v) (γ : outParam (Type w)) where
hSMul : α → β → γ
class SMul (M : Type _) (α : Type _) where
smul : M → α → α
infixr:73 " • " => HSMul.hSMul
instance instHSMul {α β : Type _} [SMul α β] : HSMul α β β where
hSMul := SMul.smul
-- note that the function `SMulZeroClass.toSMul` is what disrupts `simp` later
class SMulZeroClass (M A : Type _) extends SMul M A where
class SMulWithZero (R M : Type _) extends SMulZeroClass R M where
instance MulZeroClass.toSMulWithZero (R : Type _) [Mul R] [Zero R] : SMulWithZero R R where
smul := (· * ·)
end HSMul_stuff
section Algebra_stuff
class Algebra (R : Type u) (A : Type v) [CommSemiring R] [Semiring A] extends SMul R A,
R →+* A where
-- needed for troublesome `Algebra.instid`
def RingHom.toAlgebra' {R S} [CommSemiring R] [Semiring S] (i : R →+* S)
: Algebra R S where
smul c x := i c * x
toRingHom := i
-- needed for troublesome `Algebra.instid`
def RingHom.toAlgebra {R S} [CommSemiring R] [CommSemiring S] (i : R →+* S) : Algebra R S :=
i.toAlgebra'
-- comment this out and the failing `simp` works
instance Algebra.instid (R : Type _) [CommSemiring R] : Algebra R R :=
(RingHom.id R).toAlgebra
end Algebra_stuff
namespace Pi_stuff
instance instSMul {I : Type v} {f : I → Type w} {M : Type u} [∀ i, SMul M <| f i] : SMul M (∀ i : I, f i) :=
⟨fun s x => fun i => s • x i⟩
end Pi_stuff
section oliver_example
theorem Pi.smul_apply {I : Type} {f : I → Type} {β : Type} [∀ i, SMul β (f i)] (x : ∀ i, f i) (b : β) (i : I) : (b • x) i = b • (x i) :=
rfl
instance (R : Type) [CommRing R] : CommSemiring R where
example {R : Type} [CommRing R] : True :=
let foo := (Algebra.instid R).toSMul
let bar : SMul R R := SMulZeroClass.toSMul
have : foo = bar := rfl -- they are defeq
trivial
-- this proof works fine
example {α R : Type} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
simp only [Pi.smul_apply]
-- as does this one
example {α R : Type} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
simp only [Pi.smul_apply _]
-- the presence of `bar` messes up the first proof
-- set_option trace.Meta.isDefEq true in
example {α R : Type} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let bar : SMul R R := SMulZeroClass.toSMul
-- have qux : Algebra.toSMul = bar := rfl
fail_if_success simp only [Pi.smul_apply]
sorry
-- but not the second
example {α R : Type} [CommRing R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let bar : SMul R R := SMulZeroClass.toSMul
simp only [Pi.smul_apply _]
-- The first proof is fixed again if we use `Ring` instead of `CommRing`.
example {α R : Type} [Ring R] (f : α → R) (r : R) (a : α) :
(r • f) a = r • (f a) := by
let bar : SMul R R := SMulZeroClass.toSMul
simp only [Pi.smul_apply]
end oliver_example
(edit: removed all the monoids)
Kevin Buzzard (Aug 22 2023 at 19:22):
That's all I have time for today but hopefully it is enough to help someone who wants to get to the bottom of this random apparently incorrect defeq failure.
Kevin Buzzard (Aug 22 2023 at 19:24):
The trace of the failing defeq (obtained with set_option trace.Meta.isDefEq true
) is here. If I've not made a mistake Algebra.toSMul
and bar
are defeq but the second cross in the list below is a failure.
[Meta.isDefEq] ❌ fun i ↦ Algebra.toSMul =?= fun i ↦ bar ▼
[] ✅ α =?= α
[] ❌ Algebra.toSMul =?= bar ▼
[] ❌ Algebra.toSMul =?= SMulZeroClass.toSMul ▼
[] ❌ (Algebra.instid R).1 =?= SMulWithZero.toSMulZeroClass.1 ▼
[] ❌ (Algebra.instid R).1 =?= { smul := fun x x_1 ↦ x * x_1 } ▼
[] ✅ SMul R R =?= SMul R R
[] ❌ SMul.smul =?= fun x x_1 ↦ x * x_1 ▼
[] ❌ fun x x_1 ↦ x * x_1 =?= fun a ↦ SMul.smul a ▼
[] ✅ R =?= R
[] ❌ fun x ↦ x✝ * x =?= SMul.smul x✝ ▼
[] ❌ fun x ↦ x✝ * x =?= fun a ↦ SMul.smul x✝ a ▼
[] ✅ R =?= R
[] ❌ x✝¹ * x✝ =?= SMul.smul x✝¹ x✝ ▼
[] ❌ @HMul.hMul =?= @SMul.smul
[onFailure] ❌ x✝¹ * x✝ =?= SMul.smul x✝¹ x✝
[] ❌ Mul.mul x✝¹ x✝ =?= SMul.smul x✝¹ x✝ ▼
[] ❌ @Mul.mul =?= @SMul.smul
[onFailure] ❌ Mul.mul x✝¹ x✝ =?= SMul.smul x✝¹ x✝
[onFailure] ❌ Mul.mul x✝¹ x✝ =?= SMul.smul x✝¹ x✝
[onFailure] ❌ (Algebra.instid R).1 =?= { smul := fun x x_1 ↦ x * x_1 }
Matthew Ballard (Aug 22 2023 at 19:31):
They are but it cannot figure that out because it refuses to unfold (Algebra.instid R).1
for some reason.
Kevin Buzzard (Aug 22 2023 at 19:49):
My understanding of the trace is that (Algebra.instid R).1
is being unfolded, and what is failing is x✝¹ * x✝ =?= SMul.smul x✝¹ x✝
for some reason. Lean seems to want to deduce this from @HMul.hMul =?= @SMul.smul
which I don't think is true in the generality it asks?
Kevin Buzzard (Aug 22 2023 at 19:52):
This failure here
[] ❌ x✝¹ * x✝ =?= SMul.smul x✝¹ x✝
with pp.all
on is @HMul.hMul R R R instHMul x✝¹ x✝ =?= @SMul.smul R R (Algebra.instid R).1 x✝¹ x✝
... aah, maybe I see what you're getting at...
Matthew Ballard (Aug 22 2023 at 19:52):
The fact it flipped it to the other side is also part of the indication it doesn't want to unfold it
Kevin Buzzard (Aug 25 2023 at 11:21):
Patrick Massot (Sep 12 2023 at 15:02):
Is the following a new example or more of the same?
example {α β : Type} {f : α × β → β → β} (h : ∀ p : α × β, f p p.2 = p.2)
(a : α) (b : β) : f (a, b) b = b := by
-- simp [h] -- fails
rw [h] -- works
It seems especially simple. Fun fact: you can copy-paste this example into either Lean 3 or Lean 4 with no difference (except of course that simp
will fail in Lean 4 and succeed in Lean 3).
Matthew Ballard (Sep 12 2023 at 15:38):
Can one change the transparency level unfolded for simp
in some way? I didn't see it docs#Lean.Meta.Simp.Config
Matthew Ballard (Sep 12 2023 at 15:39):
Tracing is not giving a lot to work with
Kevin Buzzard (Sep 12 2023 at 15:40):
At least I don't have to spend 5 hours making it mathlib-free though :D This is a great example and I wonder how much other stuff it explains.
Kevin Buzzard (Sep 12 2023 at 15:42):
Note that the standard hacks simp [(h)]
and simp [h _]
don't fix the problem.
Kevin Buzzard (Sep 12 2023 at 15:43):
Even simp [h (a, b)]
fails.
Matthew Ballard (Sep 12 2023 at 15:46):
Related
example {α β : Type} {f : α × β → β → β}
(a : α) (b : β) (h : f (a,b) (a,b).2 = (a,b).2) : f (a, b) b = b := by
-- simp [h] -- fails
rw [h] -- works
Matthew Ballard (Sep 12 2023 at 15:48):
Also
example {α β : Type} {f : α × β → β → β}
(a : α) (b : β) (h : f (a,b) (a,b).2 = b) : f (a, b) b = b := by
-- simp [h] -- fails
rw [h] -- works
Matthew Ballard (Sep 12 2023 at 15:49):
example {α β : Type} {f : α × β → β → β}
(a : α) (b : β) (h : f (a,b) b = (a,b).2) : f (a, b) b = b := by
simp [h] -- works!
Damiano Testa (Sep 12 2023 at 15:50):
Using Lean 3 and looking at the proof terms constructed with rw
and simp
, I guess that simp
is using some auto-generated congr-lemmas, maybe? (Those lovely ᾰ
s make me suspect this.)
lemma As {α β : Type} {f : α × β → β → β} (h : ∀ p : α × β, f p p.2 = p.2)
(a : α) (b : β) : f (a, b) b = b := by
simp [h]
lemma Ar {α β : Type} {f : α × β → β → β} (h : ∀ p : α × β, f p p.2 = p.2)
(a : α) (b : β) : f (a, b) b = b := by
rw [h]
#print As
/-
theorem As : ∀ {α β : Type} {f : α × β → β → β},
(∀ (p : α × β), f p p.snd = p.snd) → ∀ (a : α) (b : β), f (a, b) b = b :=
λ {α β : Type} {f : α × β → β → β} (h : ∀ (p : α × β), f p p.snd = p.snd) (a : α) (b : β),
(id_tag tactic.id_tag.simp
(((λ (a a_1 : β) (e_1 : a = a_1) (ᾰ ᾰ_1 : β) (e_2 : ᾰ = ᾰ_1), congr (congr_arg eq e_1) e_2) (f (a, b) b)
b
(h (a, b))
b
b
(eq.refl b)).trans
(propext (eq_self_iff_true b)))).mpr
trivial
-/
#print Ar
/-
theorem Ar : ∀ {α β : Type} {f : α × β → β → β},
(∀ (p : α × β), f p p.snd = p.snd) → ∀ (a : α) (b : β), f (a, b) b = b :=
λ {α β : Type} {f : α × β → β → β} (h : ∀ (p : α × β), f p p.snd = p.snd) (a : α) (b : β),
(id_tag tactic.id_tag.rw (eq.rec (eq.refl (f (a, b) b = b)) (h (a, b)))).mpr (eq.refl (a, b).snd)
-/
Matthew Ballard (Sep 12 2023 at 15:53):
Speaking of terms
example {α β : Type} {f : α × β → β → β}
(a : α) (b : β) (h : f (a,b) (a,b).2 = b) : f (a, b) b = b := h -- works
Kyle Miller (Sep 12 2023 at 15:55):
@Damiano Testa congr lemmas are how simp navigates expressions. The h (a, b)
in the term is what it's doing to the LHS of the eq
and the eq.refl b
is what it's doing to the RHS. You can also read off that h (a, b)
is for turning f (a, b) b
into b
.
Damiano Testa (Sep 12 2023 at 15:56):
Matthew Ballard said:
Speaking of terms
example {α β : Type} {f : α × β → β → β} (a : α) (b : β) (h : f (a,b) (a,b).2 = b) : f (a, b) b = b := h -- works
Automation is good, but the human touch is still out of reach...
Matthew Ballard (Sep 12 2023 at 15:57):
example {α β : Type} {f : β → β}
(a : α) (b : β) (h : f (a,b).2 = b) : f b = b := by
simp [h] -- fails
Damiano Testa (Sep 12 2023 at 15:59):
Matt, do you have an import for the working example in the last batch? I get a no progress
error.
Matthew Ballard (Sep 12 2023 at 16:00):
No imports. Only the first should work but typos might very easily creep in
Matthew Ballard (Sep 12 2023 at 16:01):
I guess I did something else because I get the error copying it back in...
Damiano Testa (Sep 12 2023 at 16:01):
Ok, in your last batch, none of them work for me, not the first, nor the last, because of unit
.
Matthew Ballard (Sep 12 2023 at 16:01):
Trimmed
Matthew Ballard (Sep 12 2023 at 16:04):
example {α β : Type} {f : β → β}
(a : α) (b : β) (h : f (a,b).2 = b) : f (a,b).2 = b := by
simp [h] -- goal is now f b = b and is not closed
Damiano Testa (Sep 12 2023 at 16:04):
You can also dispense with the two types:
example {α : Type} {f : α → α}
(a b : α) (h : f (a,b).2 = b) : f b = b := by
simp [h] -- fails
Matthew Ballard (Sep 12 2023 at 16:05):
How does simp
reduce the extra simp args?
Damiano Testa (Sep 12 2023 at 16:06):
No progress above and here:
example {f : Prop → Prop}
(a b : Prop) (h : f (a,b).2 = b) : f b = b := by
simp [h] -- simp made no progress
Matthew Ballard (Sep 12 2023 at 16:07):
Unit
?
Damiano Testa (Sep 12 2023 at 16:09):
example {f : Unit → Unit}
(a b : Unit) (h : f (a,b).2 = b) : f b = b := by
simp [h] -- works!
Damiano Testa (Sep 12 2023 at 16:10):
The proof term is back at eq_self
:
theorem A : ∀ {f : Unit → Unit} (a b : Unit), f (a, b).snd = b → f b = b :=
fun {f} a b h ↦ of_eq_true (eq_self (f b))
Matthew Ballard (Sep 12 2023 at 16:14):
set_option trace.Debug.Meta.Tactic.simp
is little more chatty. It says no theorems found for rewriting f (a,b) b
Damiano Testa (Sep 12 2023 at 16:17):
Turns out that also b
is a red herring:
theorem A {f : Prop → Prop}
(a : Prop) (h : f (a,a).2 = a) : f a = a := by
simp [h] -- no progress
Matthew Ballard (Sep 12 2023 at 16:18):
Bool
fails if you want a "small" type
Eric Wieser (Sep 12 2023 at 16:27):
Patrick Massot said:
Is the following a new example or more of the same?
I'm not sure I'd expect either simp
or rw
to work here; there's no syntactic match.
Damiano Testa (Sep 12 2023 at 16:27):
If you use simp_all
this is what you get:
theorem B {f : Prop → Prop}
(a : Prop) (h : f (a,a).1 = a) : f a = a := by
simp_all [h] -- works
#print B
/-
theorem B : ∀ {f : Prop → Prop} (a : Prop), f (a, a).fst = a → f a = a :=
fun {f} a h ↦ of_eq_true (Eq.trans (congrFun (congrArg Eq (id (id h))) a) (eq_self a))
-/
Matthew Ballard (Sep 12 2023 at 16:37):
It doesn't seem to work on Patrick's original example. What does simp_all
do?
Damiano Testa (Sep 12 2023 at 16:37):
Here is also an example, after Eric's comment:
example {f : Prop → Prop}
(a : Prop) (h : f (id a) = a) : f a = a := by
simp [h] -- no progress
In this case, also rw
fails, though (and of course exact h
does work).
Damiano Testa (Sep 12 2023 at 16:38):
I only know what simp_all
says when you hover over it:
`simp_all` is a stronger version of `simp [*] at *`
where the hypotheses and target are simplified multiple times
until no simplication is applicable.
Only non-dependent propositional hypotheses are considered.
Matthew Ballard (Sep 12 2023 at 16:45):
Damiano Testa said:
Here is also an example, after Eric's comment:
example {f : Prop → Prop} (a : Prop) (h : f (id a) = a) : f a = a := by simp [h] -- no progress
In this case, also
rw
fails, though (and of courseexact h
does work).
This is a transparency issue. Using
@[reducible]
def myid {A : Type} (a : A) : A := a
works for all tactics
Eric Wieser (Sep 12 2023 at 16:47):
I'd argue this is a transparency feature, not an issue
Eric Wieser (Sep 12 2023 at 16:47):
If tactics randomly treat things as transparent then transparency controls become useless (which was pretty much the case in Lean 3)
Matthew Ballard (Sep 12 2023 at 16:48):
Ok. I meant "the failure is due to transparency settings". :wink:
Patrick Massot (Sep 12 2023 at 16:49):
I prefer a useful simp
with useless transparency controls rather than a useless simp
with "useful" transparency controls.
Matthew Ballard (Sep 12 2023 at 16:50):
But this gets back to my continuing confusion here and in lean4#2461. I thought that simp
and rw
shared the same default transparency setting (note do not read as transparency setting is .default
). It is easy to see that in the rw
config but I don't know where to look for simp
. If they do share the same default, then I would expect some of these to succeed.
Matthew Ballard (Sep 12 2023 at 16:55):
I think transparency controls are good and this is something else. But I am just talking out my :peach: atm
Oliver Nash (Sep 12 2023 at 17:02):
I think we should make an effort to highlight just how much we care about the issue of simp not firing when we would like, to the Lean developers. I spoke with @Sebastian Ullrich in person at LFTCM last week and I mentioned https://github.com/leanprover/lean4/issues/2461 (I was very impressed that he knew exactly which issue I meant from the vague description I gave!) The impression I got was that we have not conveyed how big a deal simp not firing is for us.
I also note that we haven't really addressed Sebastian's question here and we probably should.
I'd like to say more but I have to run and cook dinner now!
Patrick Massot (Sep 12 2023 at 17:03):
I think the answer to this question is: no there is no such policy.
Patrick Massot (Sep 12 2023 at 17:04):
And I agree that this simp issue is really crucial. I'm porting the sphere eversion project and this issue is using 90% of my time.
Matthew Ballard (Sep 12 2023 at 17:04):
Assuming I didn't make a silly mistake
inductive MyProd (α β : Type) where
| intro (a : α) (b : β) : MyProd α β
@[reducible]
def MyProd.snd {α β : Type} (p : MyProd α β) : β :=
match p with
| intro _ b => b
example {α β : Type} {f : β → β}
(a : α) (b : β) (h : f (MyProd.intro a b).snd = b) : f b = b := by
simp [h] -- fails
-- rw [h] -- works
Patrick Massot (Sep 12 2023 at 17:07):
What would be really really nice would be to have a tactic simp!
that would work as the Lean 3 simp
and a variant simp!?
that would flag lemmas that required extra work, hopefully with advice about how to rewrite a better version.
Mario Carneiro (Sep 12 2023 at 17:08):
you know simp!
already exists, right?
Patrick Massot (Sep 12 2023 at 17:11):
No, I don't.
Patrick Massot (Sep 12 2023 at 17:11):
But it doesn't help with my example, so it's something different.
Patrick Massot (Sep 12 2023 at 17:12):
And I can't learn what it does since it displays the docstring of simp
and jump to definition jumps to simp
.
Mario Carneiro (Sep 12 2023 at 17:12):
simp‼
Mario Carneiro (Sep 12 2023 at 17:13):
the docstring of simp
should mention the !
option
Mario Carneiro (Sep 12 2023 at 17:13):
I think it did in lean 3
Mario Carneiro (Sep 12 2023 at 17:14):
I guess not
Patrick Massot (Sep 12 2023 at 17:14):
I want simp☢️
that tries really hard.
Mario Carneiro (Sep 12 2023 at 17:14):
there is a short note in the lean 3 changelog:
- Add
iota_eqn : bool
field tosimp_config
.simp {iota_eqn := tt}
uses
all non trivial equation lemmas generated by equation/pattern-matching compiler.
A lemma is considered non trivial if it is not of the formforall x_1 ... x_n, f x_1 ... x_n = t
and
a proof by reflexivity.simp!
is a shorthand forsimp {iota_eqn := tt}
.
For example, given the goal... |- [1,2].map nat.succ = t
,simp!
reduces the left-hand-side
of the equation to[nat.succ 1, nat.succ 2]
. In this example,simp!
is equivalent to
simp [list.map]
.
James Gallicchio (Sep 12 2023 at 17:15):
Patrick Massot said:
I want
simp☢️
that tries really hard.
but which isn't aesop? :joy:
Kevin Buzzard (Sep 12 2023 at 17:40):
Does Aesop know and use all simp lemmas?
James Gallicchio (Sep 12 2023 at 17:47):
only thing is I'm not sure how good it is as a non-finishing tactic. I think Jannis is working on that?
Eric Wieser (Sep 12 2023 at 19:00):
Patrick Massot said:
I think the answer to this question is: no there is no such policy.
This is in answer to
Is there a mathlib policy about whether two instances should be defeq without unfolding standard defs?
right?
Eric Wieser (Sep 12 2023 at 19:00):
I think the answer is "yes, we almost have such a policy, there's a library note about putting @[reducible]
on non-instance def
s"
Matthew Ballard (Sep 12 2023 at 19:32):
Sounds like we should see if a reducible RingHom.toAlgebra
will make our lives better. Nothing breaks quickly
Eric Wieser (Sep 12 2023 at 19:42):
I think we might still run into trouble because RingHom.id
is not (and should not be) reducible
Matthew Ballard (Sep 12 2023 at 19:44):
I think it might reap some nice performance benefits even if it doesn't address the problems in this thread
Eric Wieser (Sep 12 2023 at 19:46):
I can confirm that making toAlgebra
reducible doesn't help with the mwe
Last updated: Dec 20 2023 at 11:08 UTC