Zulip Chat Archive
Stream: new members
Topic: Ring tactic and exponents
Benjamin (May 19 2025 at 21:43):
Is there a reason why the ring tactic cannot close goals like 2^n*3^n = 6^n or 2^(2*n) = 4^n, where n : Nat? I know that exponentiation is not a ring operation, but ring seems to have some basic support for exponents. Is this intentional? And is there some other tactic that can solve such goals?
Bjørn Kjos-Hanssen (May 19 2025 at 22:00):
This works, but I'm also surprised that omega does not find Nat.mul_pow here.
import Mathlib
example (n : ℕ) : 2^n * (3^n) = 6^n := by
rw [show 6 = 2 * 3 by omega, Nat.mul_pow]
Eric Wieser (May 19 2025 at 22:02):
You can use the module tactic...
Eric Wieser (May 19 2025 at 22:03):
import Mathlib
example {n : ℕ} : 2^n*3^n = 6^n := by
rw [show 6 = 3 * 2 from rfl]
apply Additive.ofMul.injective
simp [-Nat.reduceMul]
module
Eric Wieser (May 19 2025 at 22:03):
(as long as you factor everything first)
Benjamin (May 19 2025 at 22:04):
Thanks, but I'm more interested in how to solve this problem in general with a tactic. The difficulty arises when the terms a^n and b^n are far apart, so I'm not sure how to rewrite using <- Nat.mul_pow. I don't think Lean has a tactic for rewriting modulo associativity.
Benjamin (May 19 2025 at 22:06):
For example, if I have a term like 2^n * x * y * 3^n, I'm not sure how to rewrite it to x * y * 6^n.
Bjørn Kjos-Hanssen (May 19 2025 at 22:10):
Actually @Eric Wieser 's method works there:
import Mathlib
example {n x y : ℕ} : 2^n* x*y * 3^n = x*y * 6^n := by
rw [show 6 = 3 * 2 from rfl]
apply Additive.ofMul.injective
simp [-Nat.reduceMul]
module
As does
import Mathlib
example {n x y : ℕ} : 2^n* x*y * 3^n = x*y * 6^n := by
rw [show 6 = 3 * 2 from rfl, Nat.mul_pow]
ring
Benjamin (May 19 2025 at 22:12):
I prefer to rewrite Nat.mul_pow the other way, otherwise I have to implement a tactic to factorize every integer within an expression, which is computationally expensive (and probably tricky to implement).
Eric Wieser (May 19 2025 at 22:28):
It's actually really quite easy to implement that :)
Benjamin (May 19 2025 at 22:32):
Can you elaborate?
Eric Wieser (May 19 2025 at 22:47):
import Mathlib
open Qq
def mkProd {u} {α : Q(Type u)} (inst : Q(One $α)) (inst : Q(Mul $α)) (xs : List Q($α)) : Q($α):=
go xs.reverse
where go : List Q($α) → Q($α)
| [] => q(1)
| [x] => x
| x :: xs =>
let rest := go xs
q($rest * $x)
local elab "factorize_tac" : tactic => do
Lean.Elab.Tactic.liftMetaFinishingTactic fun m => do
have h := .mvar m
let ⟨0, ~q((OfNat.ofNat $en : ℕ) = $rhs), h⟩ := ← inferTypeQ h
| throwError "Could not infer numeral"
let .some n := en.rawNatLit?
| throwError "Not a literal {repr en}: {m}"
have factors : List Q(Nat) := n.primeFactorsList.map Lean.toExpr
if factors.length < 2 then
Lean.Elab.throwAbortTerm
throwError "Doesn't work on on 0 1"
have new_rhs : Q(Nat) := mkProd q(inferInstance) q(inferInstance) factors
have : $new_rhs =Q $en := ⟨⟩
Lean.commitIfNoEx do
let .defEq _ := ← isDefEqQ q($rhs) q($new_rhs)
| throwError "Could not assign RHS"
h.mvarId!.assignIfDefEq q(rfl : $en = $new_rhs)
/-- This "rw_proc" expands `n` into `a * b * c`. -/
theorem Nat.eq_mul {n : ℕ} {«a * b * c»} (h : ofNat(n) = «a * b * c» := by factorize_tac) :
ofNat(n) = «a * b * c» :=
h
example : 60 + 12 + 5 = sorry := by
rw [Nat.eq_mul]
rw [Nat.eq_mul (n := nat_lit 12)]
Eric Wieser (May 19 2025 at 22:47):
Eric Wieser said:
It's actually really quite easy to implement that :)
I should clarify; this is easy for me because it is very similar to something I implemented recently
Benjamin (May 19 2025 at 22:49):
Oh, wow, thanks! Did you get this code from somewhere or is it just from a personal project?
Eric Wieser (May 19 2025 at 22:50):
It's derived from #24155, which in turn came from some Lean 3 code I wrote years ago
Benjamin (May 19 2025 at 22:53):
How can I apply this rewrite to every number in an expression? simp [eq_mul] doesn't work for some reason.
tactic 'simp' failed, nested error:
maximum recursion depth has been reached
Benjamin (May 19 2025 at 22:56):
I think it's trying to repeatedly factorize the primes after they've already been factored.
Eric Wieser (May 19 2025 at 22:58):
Indeed, this hack doesn't work with simp
Eric Wieser (May 19 2025 at 22:58):
Here's the version that works with simp but not rw:
import Mathlib
open Qq
def mkProd {u} {α : Q(Type u)} (inst : Q(One $α)) (inst : Q(Mul $α)) (xs : List Q($α)) : Q($α):=
go xs.reverse
where go : List Q($α) → Q($α)
| [] => q(1)
| [x] => x
| x :: xs =>
let rest := go xs
q($rest * $x)
simproc_decl Nat.eq_mul' (_) := .ofQ fun u α e => do
match u, α, e with
| 1, ~q(ℕ), ~q(OfNat.ofNat $en) => do
let .some n := en.rawNatLit? | return .continue
have factors : List Q(Nat) := n.primeFactorsList.map Lean.toExpr
if factors.length < 2 then
return .continue
have new_rhs : Q(Nat) := mkProd q(inferInstance) q(inferInstance) factors
have : $new_rhs =Q $en := ⟨⟩
return .continue <| some <| .mk q($new_rhs) none
| _, _, _ => return .continue
example : 60 + 12 + 5 = sorry := by
simp_rw [Nat.eq_mul']
-- sorry is now cursed lol
Eric Wieser (May 19 2025 at 22:59):
( for the weird sorry behavior)
Benjamin (May 19 2025 at 22:59):
I think for my application, I can just define something like this:
theorem factorize_exp {n m : ℕ} {«a * b * c»} (h : ofNat(n) = «a * b * c» := by factorize_tac) :
ofNat(n) ^ m = «a * b * c» ^ m := by rw [h]
Benjamin (May 19 2025 at 22:59):
And then do repeat rw [factorize_exp]
Eric Wieser (May 19 2025 at 23:00):
I think Nat.eq_mul' above should work for you, if you want simp
Benjamin (May 19 2025 at 23:00):
How does simp only compare to repeat rw?
Benjamin (May 19 2025 at 23:01):
Or simp_rw?
Eric Wieser (May 19 2025 at 23:01):
simp_rw [x, y] is simp only [x]; simp only [y], roughly
Eric Wieser (May 19 2025 at 23:01):
The main consideration here is that Nat.eq_mul is a horrible hack, but Nat.eq_mul' is using the system as designed
Benjamin (May 19 2025 at 23:03):
So the simp version is better, you mean?
Eric Wieser (May 19 2025 at 23:04):
For what you're doing, yes
Benjamin (May 19 2025 at 23:05):
And what if I only want to rewrite a numeral when it occurs as the base of an exponential expression, like 60^n? I can do that with eq_mul as I described, but what about with eq_mul'?
Eric Wieser (May 19 2025 at 23:07):
Oh, I guess it's neat that you were able to adapt the unprimed version without touching any meta code
Last updated: Dec 20 2025 at 21:32 UTC