Zulip Chat Archive

Stream: maths

Topic: hyperoperation rw not rewriting properly


Mark Andrew Gerads (Jan 09 2023 at 17:01):

Can anyone tell me why the commented rw is not rewriting, and what work-around there might be?

import algebra.group_with_zero.defs

def hyperoperation :       
| 0 _ b := 1 + b
| 1 a 0 := a
| 2 _ 0 := 0
| _ _ 0 := 1
| (n + 1) a (b + 1) := hyperoperation n a (hyperoperation (n + 1) a b)

lemma hyperoperation_1_addition (a b : ) : hyperoperation 1 a b = a + b :=
begin
  induction b with bn bih,
  rw [nat_add_zero a, hyperoperation],
  {
    rw hyperoperation,
    have h1 : 0 + 1 = 1,
    exact rfl,
    rw [h1,bih],
    -- rw hyperoperation, --should work; does not work
    sorry,
  },
end

Alex J. Best (Jan 09 2023 at 17:14):

I guess the issue is somehow caused by having all these overlapping patterns, maybe lean gives up generating all the equation lemmas or something:
If I change your definition like this I think it means the same thing and lean is now happy to rewrite, but of course you should make sure the function still does what you intend

import algebra.group_with_zero.defs

def hyperoperation :       
| 0 _ b := 1 + b
| 1 a 0 := a
| 2 _ 0 := 0
| (a + 3) _ 0 := 1
| (n + 1) a (b + 1) := hyperoperation n a (hyperoperation (n + 1) a b)

lemma hyperoperation_1_addition (a b : ) : hyperoperation 1 a b = a + b :=
begin
  induction b with bn bih,
  rw [nat_add_zero a, hyperoperation],
  {
    rw hyperoperation,
    have h1 : 0 + 1 = 1,
    exact rfl,
    rw [h1,bih],
    rw [hyperoperation],
  },
end

Eric Wieser (Jan 09 2023 at 17:40):

You can have a look at the rewrite rules it generated with #print prefix hyperoperation

Mark Andrew Gerads (Jan 09 2023 at 18:25):

I found another case where it does not rewrite correctly.

import algebra.group_with_zero.defs
import tactic

def hyperoperation :       
| 0 _ b := b + 1
| 1 a 0 := a
| 2 _ 0 := 0
| (n + 3) _ 0 := 1
| (n + 1) a (b + 1) := hyperoperation n a (hyperoperation (n + 1) a b)

lemma hyperoperation_1_addition (a b : ) : hyperoperation 1 a b = a + b :=
begin
  induction b with bn bih,
  rw [nat_add_zero a, hyperoperation],
  {
    rw hyperoperation,
    rw (show 0 + 1 = 1, by refl),
    rw bih,
    rw hyperoperation,
    rw (show bn.succ = bn + 1, by refl),
    exact nat.add_assoc a bn 1,
  },
end

lemma hyperoperation_2_multiplication (a b : ) : hyperoperation 2 a b = a * b :=
begin
  induction b with bn bih,
  {
    rw hyperoperation,
    exact (nat.mul_zero a).symm,
  },
  {
    rw [hyperoperation,hyperoperation_1_addition],
    rw (show 1 + 1 = 2, by refl),
    rw bih,
    ring,
  },
end

lemma hyperoperation_succn_2_2_eq_4 (n : ) : hyperoperation (n + 1) 2 2 = 4 :=
begin
  induction n with nn nih,
  {
    rw (show 0 + 1 = 1, by refl),
    rw hyperoperation_1_addition,
  },
  {
    rw (show nn.succ + 1 = nn + 2, by refl),
    -- rw hyperoperation, --should work, fails to work
    sorry,
  },
end

Scott Morrison (Jan 09 2023 at 20:40):

I would recommend adding an intermediate layer of lemmas, that explicitly state the equation lemmas you are expecting/hoping the equation compiler is going to produce for you, and rewrite via those rather than by hyperoperation directly. That will hopefully be easier to debug and more stable to changes to the definition / moving to Lean 4.

Mark Andrew Gerads (Jan 10 2023 at 00:30):

I added all the intermediate lemmas, yet 1 of them (the one I need) stubbornly resists my attempt to unsorry it.

import algebra.group_with_zero.defs
import tactic

def hyperoperation :       
| 0 _ b := b + 1
| 1 a 0 := a
| 2 _ 0 := 0
| (n + 3) _ 0 := 1
| (n + 1) a (b + 1) := hyperoperation n a (hyperoperation (n + 1) a b)

-- Basic hyperoperation lemmas

lemma hyperoperation_0ab_b.succ (a b : ) : hyperoperation 0 a b = b + 1 :=
begin
  rw hyperoperation,
end

lemma hyperoperation_1a0_a (a : ) : hyperoperation 1 a 0 = a :=
begin
  rw hyperoperation,
end

lemma hyperoperation_2a0_0 (a : ) : hyperoperation 2 a 0 = 0 :=
begin
  rw hyperoperation,
end

lemma hyperoperation_n3a0_1 (n a : ) : hyperoperation (n + 3) a 0 = 1 :=
begin
  rw hyperoperation,
end

lemma hyperoperation_n1ab1_recurse (n a b : ) :
  hyperoperation (n + 1) a (b + 1) = hyperoperation n a (hyperoperation (n + 1) a b) :=
begin
  -- rw hyperoperation, -- does not work; should work
  sorry,
end

Junyan Xu (Jan 10 2023 at 01:13):

You simply need to do some cases; since (n+1) overlaps with other cases where n = 1 or 2, Lean breaks equations into multiple cases, which you can see using #print prefix hyperoperation.

lemma hyperoperation_n1ab1_recurse (n a b : ) :
  hyperoperation (n + 1) a (b + 1) = hyperoperation n a (hyperoperation (n + 1) a b) :=
begin
  cases n,
  rw hyperoperation,
  cases n;
  rw hyperoperation,
end

Junyan Xu (Jan 10 2023 at 01:16):

The relevant ones used in the above proof are

hyperoperation.equations._eqn_3 :  (a b : ), hyperoperation (0 + 1) a (b + 1) = hyperoperation 0 a (hyperoperation (0 + 1) a b)
hyperoperation.equations._eqn_5 :  (a b : ), hyperoperation (1 + 1) a (b + 1) = hyperoperation 1 a (hyperoperation (1 + 1) a b)
hyperoperation.equations._eqn_7 :  (n a b : ),
  hyperoperation (n.succ.succ + 1) a (b + 1) = hyperoperation n.succ.succ a (hyperoperation (n.succ.succ + 1) a b)

Mark Andrew Gerads (Jan 10 2023 at 01:58):

Okay, thank you. I think I have all the interesting hyperoperation lemmas now.
Any help or advice on getting it added to mathlib would be appreciated. I do not know what would be the best place to add the file (maybe data.nat.hyperoperation ?), for example, and I am unsure how to make the branch for the pull request. Also, I do not know if my lemma names are good enough.
Here is the completed hyperoperation file:

import tactic

def hyperoperation :       
| 0 _ b := b + 1
| 1 a 0 := a
| 2 _ 0 := 0
| (n + 3) _ 0 := 1
| (n + 1) a (b + 1) := hyperoperation n a (hyperoperation (n + 1) a b)

-- Basic hyperoperation lemmas

lemma hyperoperation_0ab_b.succ (a b : ) : hyperoperation 0 a b = b + 1 :=
begin
  rw hyperoperation,
end

lemma hyperoperation_1a0_a (a : ) : hyperoperation 1 a 0 = a :=
begin
  rw hyperoperation,
end

lemma hyperoperation_2a0_0 (a : ) : hyperoperation 2 a 0 = 0 :=
begin
  rw hyperoperation,
end

lemma hyperoperation_n3a0_1 (n a : ) : hyperoperation (n + 3) a 0 = 1 :=
begin
  rw hyperoperation,
end

lemma hyperoperation_n1ab1_recurse (n a b : ) :
  hyperoperation (n + 1) a (b + 1) = hyperoperation n a (hyperoperation (n + 1) a b) :=
begin
  cases n,
  rw hyperoperation,
  cases n;
  rw hyperoperation,
end

-- Interesting hyperoperation lemmas

lemma hyperoperation_1_addition (a b : ) : hyperoperation 1 a b = a + b :=
begin
  induction b with bn bih,
  {
    rw [nat_add_zero a, hyperoperation_1a0_a],
  },
  {
    rw [hyperoperation_n1ab1_recurse,bih,hyperoperation_0ab_b.succ],
    exact nat.add_assoc a bn 1,
  },
end

lemma hyperoperation_2_multiplication (a b : ) : hyperoperation 2 a b = a * b :=
begin
  induction b with bn bih,
  {
    rw hyperoperation_2a0_0,
    exact (nat.mul_zero a).symm,
  },
  {
    rw [hyperoperation_n1ab1_recurse,hyperoperation_1_addition,bih],
    ring,
  },
end

lemma hyperoperation_3_exponentiation (a b : ) : hyperoperation 3 a b = a ^ b :=
begin
  induction b with bn bih,
  {
    rw hyperoperation_n3a0_1,
    exact (pow_zero a).symm,
  },
  {
    rw [hyperoperation_n1ab1_recurse,hyperoperation_2_multiplication,bih],
    exact (pow_succ a bn).symm,
  },
end

lemma hyperoperation_n2a1_a (n a : ) : hyperoperation (n + 2) a 1 = a :=
begin
  induction n with nn nih,
  {
    rw hyperoperation_2_multiplication,
    ring,
  },
  {
    rw [hyperoperation_n1ab1_recurse,hyperoperation_n3a0_1,nih],
  },
end

lemma hyperoperation_succn_2_2_eq_4 (n : ) : hyperoperation (n + 1) 2 2 = 4 :=
begin
  induction n with nn nih,
  {
    rw hyperoperation_1_addition,
  },
  {
    rw [hyperoperation_n1ab1_recurse,hyperoperation_n2a1_a,nih],
  },
end

Mark Andrew Gerads (Jan 10 2023 at 02:22):

After reading https://leanprover-community.github.io/contribute/style.html , I supposed I should replace a and b with m and k.

Junyan Xu (Jan 10 2023 at 03:07):

I think the next challenge is to show monotone and strict_mono w.r.t. the three arguments, whenever applicable. And the relationship with the Ackermann function which is already in mathlib as docs#ack. There's also relationship with Knuth's up-arrow and Conway's chained arrow notation, which however aren't in mathlib yet. (Personally, I have some computation using Knuth's arrows that I may formalize sometime ...)

Mark Andrew Gerads (Jan 10 2023 at 04:07):

PR now awaiting review.
https://github.com/leanprover-community/mathlib/pull/18116


Last updated: Dec 20 2023 at 11:08 UTC