Zulip Chat Archive
Stream: mathlib4
Topic: Lots of 1 + 1 + 1 + 1
Heather Macbeth (Jan 27 2023 at 17:43):
Mario Carneiro said:
Thomas Murrills said:
One of the reasons might be the following:
simp
expands natural numbers into1 + 1 + 1 + ... + 1
when they appear as arguments to the argument ofInt.ofNat
(sometimes). For example,example (x : ℤ) : x = Int.ofNat (12 * 2) := by simp
leaves us with the goal
x = (1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1) * (1 + 1)
This is certainly a problem (which has been mentioned elsewhere although I don't think it has a dedicated issue for it), although I don't think it is specific to
Int.ofNat
. I think you can cause the same behavior usingNat.cast
, it's a problem insimp
and/or our simp set.
Some tactics currently produce a lot of 1 + 1 + 1 ...
@Thomas Murrills and @Mario Carneiro were just discussing this in the context of making norm_num
work for OfScientific
. And @Rob Lewis and I encountered this when using zify
.
Mario pointed out that we should have a dedicated issue for this, which I would be happy to open, except that I don't understand the underlying cause. Mario, do you mean that somewhere there is a lemma wrongly tagged @[simp]
, and this needs to be tracked down?
Mario Carneiro (Jan 27 2023 at 17:45):
My working theory is that simp is applying the lemma \u (n + 1) = \u n + 1
to \u 5
by solving the unification problem 5 =?= ?n + 1
-> ?n := 4
. This seems to be over-aggressive in this case, although I can see use cases for it when unfolding recursive functions on numerals
Mario Carneiro (Jan 27 2023 at 17:47):
One possible fix is just to say that \u (n + 1) = \u n + 1
is no longer allowed to be a default simp lemma and it has to be applied explicitly
Mario Carneiro (Jan 27 2023 at 17:48):
Another observation is that this should not be a legal unification at reducible transparency, because 4 + 1 = 5
is not (should not be?) a reducible rfl
Heather Macbeth (Jan 27 2023 at 17:52):
Interesting. I'd like to keep using simp
with lemmas expressing (complicated) functions f (n + 1)
in terms of f n
, the way we could in mathlib3, like:
import algebra.big_operators.basic
open_locale big_operators
example : ∑ i in finset.range 5, i = 10 := by simp [finset.sum_range_succ]
Heather Macbeth (Jan 27 2023 at 17:52):
So it seems like we want a fix which is targeted to ↑
.
Heather Macbeth (Jan 27 2023 at 17:53):
Mario Carneiro said:
One possible fix is just to say that
\u (n + 1) = \u n + 1
is no longer allowed to be a default simp lemma and it has to be applied explicitly
Like this, if you think it would work.
Heather Macbeth (Jan 27 2023 at 17:55):
Although I suppose that in the example i just gave, finset.sum_range_succ
was not a simp lemma, I had to provide it explicitly!
Jireh Loreaux (Jan 27 2023 at 20:15):
Heather, we can't target ↑
specifically anymore though, or do you mean Int.ofNat
?
Heather Macbeth (Jan 27 2023 at 20:18):
Please take what I wrote as musing rather than a concrete suggestion; I don't understand the mathlib4 coercion setup yet!
Thomas Murrills (Jan 27 2023 at 22:24):
Here's one possible solution I found. It seems that simp
tries Nat.cast_ofNat
first, but the synthesized instance doesn't match the inferred instance, so it then tries Nat.cast_succ
, which peels off a + 1
. (This is the same issue that makes many Invertible
lemmas unwieldy.)
It seems that using type inference instead and changing [NatCast R]
to {_ : NatCast R}
in the arguments to the Nat.cast_ofNat
lemma (in Mathlib.Data.Nat.Cast.Defs
) fixes the problem in the couple of cases I've tried:
@[simp, norm_cast] theorem Nat.cast_ofNat {_ : NatCast R} [Nat.AtLeastTwo n] :
(Nat.cast (OfNat.ofNat n) : R) = OfNat.ofNat n := rfl
However, I know there are performance issues with ofNat
(hence norm_num
), so I don't know if this is a good solution. (Also it doesn't fix whatever's wrong with the norm_num
extension I'm working on, so there's still something going on!)
Yury G. Kudryashov (Jan 27 2023 at 22:57):
Is there a document that describes how do ofNat
, NatCast
, Zero
, One
, Nat.atLeastTtwo
etc interact?
Gabriel Ebner (Jan 28 2023 at 02:39):
but the synthesized instance doesn't match the inferred instance
I've also noticed that. It seems to be because Nat.cast
is not reducible. And we have two instances for NatCast Int
: one with Int.ofNat
and one with Nat.cast
. (Where the second of course uses Int.ofNat
as well, but it's hidden behind the non-reducible Nat.cast
definition.)
Gabriel Ebner (Jan 28 2023 at 02:46):
Is there a document that describes how do ofNat, NatCast, Zero, One, Nat.atLeastTtwo etc interact?
Not really.
ofNat
is the built-in function used for numeric literals (i.e., it is never applied tox + 1
or anything)NatCast
is the coercionNat → R
Zero
andOne
are the neutral elements in additive and multiplicative structures, resp.- You should be able to write
0
and1
for the neutral elements, so we have[Zero A] : OfNat A (nat_lit 0)
and[One M] : OfNat M (nat_lit 1)
instances - You should also be able to write
42
in a ring, so we have[NatCast R] : OfNat R n
. But that would be a diamond with the0
and1
instances, so we add an extra[AtLeastTwo n]
argument to make sure the instance only gets used for numbers >= 2. Remember that0
andNat.cast 0
are not defeq in general.
Thomas Murrills (Jan 28 2023 at 03:17):
Gabriel Ebner said:
but the synthesized instance doesn't match the inferred instance
I've also noticed that. It seems to be because
Nat.cast
is not reducible. And we have two instances forNatCast Int
: one withInt.ofNat
and one withNat.cast
. (Where the second of course usesInt.ofNat
as well, but it's hidden behind the non-reducibleNat.cast
definition.)
Interesting! It seems this is a recurring issue.
Is there a reason that typeclass search doesn’t first try using type inference to get the instance? It seems like it’s going to type-infer the instance and check it for defequality to the synthesized instance when it’s possible to do so anyway, so why doesn’t typeclass search just use the available inferred instance (when it exists) instead of synthesizing something else and failing?
Thomas Murrills (Jan 28 2023 at 03:19):
Or, is the “right” solution making sure that everything has the right reducibility somehow? (I don’t know if this would take care of the problematic Invertible
simp lemmas, though.)
Gabriel Ebner (Jan 28 2023 at 03:23):
Regardless of what we do about the not-absolutely-necessary defeq check in simp, we should still make the instances defeq. Though I can never quite remember the guidelines here. Usually we compare instances using semireducible transparency (because they're implicit arguments). I don't know what's going on here.
Thomas Murrills (Jan 28 2023 at 04:00):
Hmm…in the general case, does this mean micromanaging instances to avoid diamonds?
It would be helpful if we had some more tools for handling diamonds! I can see a couple of different overall directions for such tools, but have no idea which, if any, would be useful/unproblematic:
- change typeclass search (or
simp
?) so that we prefer ordinary type inference to typeclass search for filling[]
args when possible, thus choosing the “right” instance whenever we can (would this cause performance issues? could we use some “simple” form of type inference that would be enough in most cases?) - create linters that search for diamonds (not sure if this is even a tractable problem? or, on the other hand, if it already exists in some capacity?)
- have a way of satisfying such a linter by “establishing” defequality somehow among overlapping instances in the circumstances in which they overlap (do we have this?)
- have a procedure for automatically normalizing any overlapping instances somehow once we know they’re defeq
Would any of these be useful directions to pursue as a broader project?
Thomas Murrills (Jan 28 2023 at 04:06):
(Oops, I guess I lost focus…this counts as a separate topic, I think :) )
Yury G. Kudryashov (Jan 28 2023 at 05:35):
About unification vs typeclass search for simp
. I'm porting topology.constructions
, and one of the issues I have to work around is that simp
can't unfold a definition (e.g., instTopologicalSpaceProd
) that takes [TopologicalSpace α]
if it is called with an argument which is not the instance infer_instance
can find.
Last updated: Dec 20 2023 at 11:08 UTC