Zulip Chat Archive

Stream: Is there code for X?

Topic: expand_nums tactic


Asei Inoue (Feb 23 2025 at 05:01):

How to write a tactic to expand positive integers to sum of 1s?

For example, it should rewrite 3 to 1 +1+1.

Asei Inoue (Feb 23 2025 at 05:03):

Background: I want to create my own (toy) tactic to treat expression in semiring.
I know ring tactic in mathlib, but I want to do DIY and want a leight-weight tactic for this task.

Asei Inoue (Feb 23 2025 at 05:30):

The task can be divided into two parts:

  • A tactic to identify numeric literals in an equation.
  • A tactic to expand a numeric literal n into 1 + 1 + .. + 1.

The latter is almost complete since, once we determine which numeric literals we want to expand, we can apply the following logic for expansion:

private def oneSum (n : Nat) :=
  match n with
  | 0 => 0
  | n + 1 => oneSum n + 1

private theorem Nat.oneSumEq :  (n : Nat), oneSum n = n
  | 0 => rfl
  | n + 1 => by
    rw [oneSum]
    rw [Nat.oneSumEq n]

example : 3 = 2 + 1 := by
  rw [ Nat.oneSumEq 3]
  try repeat rewrite [oneSum]
  rewrite [show 0 + 1 = 1 by rfl]
  guard_target =ₛ 1 + 1 + 1 = 2 + 1

  rfl

Yaël Dillies (Feb 23 2025 at 10:14):

This should be easy to do with a simproc, except for the "matching the numerals " part. I am not at a computer (and won't be in the foreseeable future), but I will be explaining what a simproc is and how to write one to an audience in Imperial tomorrow afternoon.

Asei Inoue (Feb 23 2025 at 10:29):

my current answer is :

import Lean

private def oneSum (n : Nat) : Nat :=
  match n with
  | 0 => 0
  | n + 1 => oneSum n + 1

private theorem Nat.oneSumEq :  (n : Nat), n = oneSum n := by
  intro n
  match n with
  | 0 => rfl
  | n + 1 =>
    rw [oneSum]
    rw [ Nat.oneSumEq n]

open Lean Elab.Tactic Elab.Term

elab "expand_numerals" : tactic => do
  for i in [2 : 20] do
    let iexpr := toExpr i
    evalTactic <|  `(tactic| try rewrite [Nat.oneSumEq $( exprToSyntax iexpr) ])

example (n : Nat) : (n + m) * (n + m) = n * n + 2 * m * n + m * m := by
  expand_numerals
  repeat rewrite [oneSum]
  simp only [Nat.add_mul, Nat.mul_add, Nat.zero_mul, Nat.mul_zero]
  ac_rfl

Asei Inoue (Mar 08 2025 at 06:26):

I don't want to manually specify the number of upper limits

Robin Arnez (Mar 10 2025 at 16:02):

This seems to work:

theorem unfoldNat (x : Nat) : no_index (OfNat.ofNat (x + 2) : Nat) = no_index (OfNat.ofNat (x + 1) : Nat) + 1 :=
  rfl

theorem unfoldNatZero (x : Nat) : no_index (OfNat.ofNat (0 + x) : Nat) = x :=
  Nat.zero_add x

example (n : Nat) : (n + m) * (n + m) = n * n + 10 * m * n + m * m := by
  simp only [unfoldNat, unfoldNatZero]
  -- ...

Asei Inoue (Mar 10 2025 at 19:53):

@Robin Arnez
Thank you!
what is no_index?

Robin Arnez (Mar 10 2025 at 19:54):

I don't remember 100% but it was some kind of thing with simp

Robin Arnez (Mar 10 2025 at 19:55):

It seems to work fine without though

Aaron Liu (Mar 10 2025 at 20:08):

Robin Arnez said:

This seems to work:

theorem unfoldNat (x : Nat) : no_index (OfNat.ofNat (x + 2) : Nat) = no_index (OfNat.ofNat (x + 1) : Nat) + 1 :=
  rfl

theorem unfoldNatZero (x : Nat) : no_index (OfNat.ofNat (0 + x) : Nat) = x :=
  Nat.zero_add x

example (n : Nat) : (n + m) * (n + m) = n * n + 10 * m * n + m * m := by
  simp only [unfoldNat, unfoldNatZero]
  -- ...

You should use the ofNat(x + 1) elaborator.

Robin Arnez (Mar 10 2025 at 20:11):

Yeah, you're right, I think it does exactly this.


Last updated: May 02 2025 at 03:31 UTC