Zulip Chat Archive
Stream: general
Topic: Panic in rw: Nat.pow exponent is too big
Eric Wieser (Apr 05 2024 at 12:41):
This works in Lean 3:
import data.nat.basic
example : 1 + 2 + 2^(2^32) = 2^(2^32) + 1 + 2 :=
begin
rw [add_right_comm],
rw [add_comm 1 _],
end
but in Lean 4 this panics:
import Mathlib.Data.Nat.Basic
import Mathlib.Algebra.Group.Basic
#eval Lean.versionString
example : 1 + 2 + 2^(2^32) = 2^(2^32) + 1 + 2 := by
rewrite [add_right_comm]
sorry
This looks to me like some numeric normalizer issue; the analogous version not in Nat
works fine:
example {α} [Semiring α] :
(1 : α) + 2 + 2^(2^32) = 2^(2^32) + 1 + 2 := by
rewrite [add_right_comm]
sorry
Eric Wieser (Apr 05 2024 at 12:41):
It's pretty unexpected to me that rewrite
is even performing this normalization; but maybe I'm misdiagnosing and this is coming from the kernel
Mario Carneiro (Apr 05 2024 at 12:47):
large closed terms of type nat are kind of like live hand grenades, because calling whnf
on them will cause them to be worked out in full, and many tactics do this when looking for structure
Eric Wieser (Apr 05 2024 at 12:49):
Is whnf
in a tactic an anti-pattern? (vs let ~q(desiredHead $args) := e
or the sugar-free alternative, for instance)
Mario Carneiro (Apr 05 2024 at 12:50):
I think even whnfR
is bad, although this may depend on the nat function
Eric Wieser (Apr 05 2024 at 12:51):
Surely whnfR
shouldn't be unfolding +
/ Nat.add
?
Mario Carneiro (Apr 05 2024 at 12:51):
there's an issue open about that (lean4#1994)
Mario Carneiro (Apr 05 2024 at 12:51):
it does
Mario Carneiro (Apr 05 2024 at 12:51):
even though Nat.add
is not reducible
Mario Carneiro (Apr 05 2024 at 12:52):
example : 2 + 2 = 4 := by with_reducible rfl
example : 2 + 2 = 4 := by with_reducible exact rfl
example : 2 + 2 = 4 := by with_reducible eq_refl
Eric Wieser (Apr 05 2024 at 12:53):
docs#OfNat.ofNat is also not reducible and yet gets unfolded here too, I guess
Mario Carneiro (Apr 05 2024 at 12:54):
just tested, 2 ^ 2 = 4
also works, so I think this is the culprit
Eric Wieser (Apr 05 2024 at 12:55):
Defining a Mathlib.Nat
that is a copy of Nat
would fix all this, right? Because whatever strange special casing that is going on would presumably not apply here?
Eric Wieser (Apr 05 2024 at 12:56):
(obviously that's a bad idea for other reasons, but panicking in a proof is pretty awful too)
Mario Carneiro (Apr 05 2024 at 12:57):
yes, let me double check what the check is
Mario Carneiro (Apr 05 2024 at 12:58):
it's a hard coded list of nat functions, including pow
:
match fn with
| ``Nat.add => reduceBinNatOp Nat.add a1 a2
| ``Nat.sub => reduceBinNatOp Nat.sub a1 a2
| ``Nat.mul => reduceBinNatOp Nat.mul a1 a2
| ``Nat.div => reduceBinNatOp Nat.div a1 a2
| ``Nat.mod => reduceBinNatOp Nat.mod a1 a2
| ``Nat.pow => reduceBinNatOp Nat.pow a1 a2
| ``Nat.gcd => reduceBinNatOp Nat.gcd a1 a2
| ``Nat.beq => reduceBinNatPred Nat.beq a1 a2
| ``Nat.ble => reduceBinNatPred Nat.ble a1 a2
| ``Nat.land => reduceBinNatOp Nat.land a1 a2
| ``Nat.lor => reduceBinNatOp Nat.lor a1 a2
| ``Nat.xor => reduceBinNatOp Nat.xor a1 a2
| ``Nat.shiftLeft => reduceBinNatOp Nat.shiftLeft a1 a2
| ``Nat.shiftRight => reduceBinNatOp Nat.shiftRight a1 a2
| _ => return none
Eric Wieser (Apr 05 2024 at 12:58):
Should this be conditioned on the reducibility being "default" or higher?
Mario Carneiro (Apr 05 2024 at 12:59):
I think it should
Eric Wieser (Apr 05 2024 at 12:59):
This optimized reduction seems like a good idea, but only in the cases where reduction should happen in the first place
Mario Carneiro (Apr 05 2024 at 13:00):
it's a bit unclear whether Nat.succ 3 = 4
should be considered a reducible defeq or not
Mario Carneiro (Apr 05 2024 at 13:00):
since Nat.succ
isn't a function with a definition in the first place
Eric Wieser (Apr 05 2024 at 13:00):
attribute [reducible] Nat.succ
is legal
Mario Carneiro (Apr 05 2024 at 13:01):
yeah but that doesn't mean anything, it has no associated defeq
Mario Carneiro (Apr 05 2024 at 13:01):
it's an opaque
Eric Wieser (Apr 05 2024 at 13:01):
Ah, I'm thinking of projections not constructors
Mario Carneiro (Apr 05 2024 at 13:02):
I guess ditto for Nat.zero = 0
Eric Wieser (Apr 05 2024 at 13:02):
Both of these should be not reducible because OfNat.ofNat
isn't reducible, right?
Mario Carneiro (Apr 05 2024 at 13:03):
I think these are operating on raw literals?
Mario Carneiro (Apr 05 2024 at 13:03):
that actually seems somewhat surprising, this is the elaborator's WHNF function
Mario Carneiro (Apr 05 2024 at 13:05):
I guess the literal-wrapping must happen somewhere else, after getting the result of the WHNF
Timo Carlin-Burns (Apr 05 2024 at 14:51):
I don't know if this is related, but I've found that in unification, ground Nat terms often get normalized into numerals. e.g.
-- `Nat.zero.succ` gets normalized to `1`:
#check (show Nat.zero.succ.succ = Nat.succ _ by with_reducible rfl)
-- Nat.zero.succ.succ = Nat.succ 1
Mario Carneiro (Apr 05 2024 at 22:25):
yes, that would definitely explain the rw
behavior, even if the matching itself is not at fault
Jeremy Tan (Apr 20 2025 at 18:13):
This alone gives (kernel) deep recursion detected
already (from IMO 2010 Q5):
lemma bomb : 2010 ^ 2010 ^ 2010 ≤ 2 ^ 2 ^ 2 ^ 2 ^ 2 ^ 11 :=
calc
_ ≤ 2 ^ (11 * 2010 ^ 2010) := by rw [pow_mul]; gcongr; norm_num
_ ≤ _ := by
apply Nat.pow_le_pow_right zero_lt_two
calc
_ ≤ 2 ^ (4 + 11 * 2010) := by rw [pow_add, pow_mul]; gcongr <;> norm_num
_ ≤ _ := by
apply Nat.pow_le_pow_right zero_lt_two
calc
_ ≤ 2 ^ 2 ^ 2 ^ 2 := by norm_num
_ ≤ _ := by gcongr <;> norm_num
Jeremy Tan (Apr 20 2025 at 18:19):
How do I get this error-free? No amount of set_option exponentiation.threshold
will work
Jeremy Tan (Apr 20 2025 at 18:34):
Is there an option to "exclude exponentiation from kernel reduction"?
Aaron Liu (Apr 20 2025 at 18:34):
I was able to hack this together
import Mathlib
lemma bomb : 2010 ^ 2010 ^ 2010 ≤ 2 ^ 2 ^ 2 ^ 2 ^ 2 ^ 11 := by
obtain ⟨pow, pow_def⟩ : ∃ (f : ℕ → ℕ → ℕ), ∀ a b, f a b = a ^ b := ⟨(· ^ ·), fun _ _ => rfl⟩
simp_rw [← pow_def]
calc
_ ≤ pow 2 (11 * pow 2010 2010) := by
rw [pow_def 2, pow_mul, pow_def]
gcongr
norm_num
_ ≤ _ := by
rw [pow_def 2, pow_def 2]
apply Nat.pow_le_pow_right zero_lt_two
calc
_ ≤ pow 2 (4 + 11 * 2010) := by
rw [pow_def 2, pow_add, pow_mul, pow_def]
gcongr <;> norm_num
_ ≤ _ := by
rw [pow_def 2, pow_def 2]
apply Nat.pow_le_pow_right zero_lt_two
calc
_ ≤ 2 ^ 2 ^ 2 ^ 2 := by norm_num
_ ≤ _ := by
repeat (rw [pow_def]; gcongr; (· norm_num); try (norm_num; done))
Jeremy Tan (Apr 20 2025 at 18:51):
We need a more elegant solution
Jeremy Tan (Apr 21 2025 at 00:59):
@Eric Wieser do you have any suggestions?
Eric Wieser (Apr 21 2025 at 05:03):
Eric Wieser said:
Defining a
Mathlib.Nat
that is a copy ofNat
would fix all this, right? Because whatever strange special casing that is going on would presumably not apply here?
We could do this, but without actually doing a global replace
Eric Wieser (Apr 21 2025 at 05:04):
That is, have a SafeNat
type that we copy all the Nat instances to, and some Equivs / induction principles to transport back and forth
Eric Wieser (Apr 21 2025 at 05:04):
This would be a leaf file in mathlib with a module doc summarizing this thread
Jz Pan (Apr 21 2025 at 06:45):
Maybe we need a mechanism temporarily marking some functions/expressions as irreducible, like Hold
in Mathematica
Jz Pan (Apr 21 2025 at 06:47):
maybe irreducible_def hold := id
, and (hold 2010) ^ (hold 2010) ^ (hold 2010) ≤ ...
.
Niels Voss (Apr 21 2025 at 08:00):
Would generalize h : 2010 = x
work for this?
Last updated: May 02 2025 at 03:31 UTC