Zulip Chat Archive
Stream: new members
Topic: Using (-1) between N and Z
Divya Ranjan (Dec 01 2024 at 01:54):
I'm trying to prove by induction the definition of a function that is recursively defined and uses , somehow even though I'm getting 2 = 2 rfl
claims they are not definitionally equivalent.
Here's the lemma:
import Mathlib
--set_option diagnostics true
open Finset BigOperators
open Ring
open Nat
def f (n : Nat) : ℤ :=
match n with
| 0 => 2
| 1 => 1
| 2 => 5
| (n + 1) => f n + 2 * f (n - 1)
#check (f)
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
· simp
#check 2
rw [show f 0 = 2 by rfl]
I've tried using abs
in the lemma statement, or using functions like natAbs
but that still doesn't work, despite the fact that the #check 2
shows that is of type
Julian Berman (Dec 01 2024 at 02:00):
I think this is saying f 0
is indeed 2
but the proof is not rfl
. I don't recall the technical details, it's something like that def
gives you something that is partially irreducible. I think perhaps you simply want abbrev
instead, in which case the proof will be rfl:
abbrev f : ℕ → ℤ
| 0 => 2
| 1 => 1
| 2 => 5
| (n + 1) => f n + 2 * f (n - 1)
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
· have : f 0 = 2 := rfl
simp
rw [show f 0 = 2 from rfl]
· sorry
Or alternatively if you do want a def you can define some API for f
.
Julian Berman (Dec 01 2024 at 02:01):
(In your original code rw [show f 0 = 2 by simp only [f]]
will work as well for unfolding f
.)
Divya Ranjan (Dec 01 2024 at 02:05):
Julian Berman said:
I think this is saying
f 0
is indeed2
but the proof is notrfl
. I don't recall the technical details, it's something like thatdef
gives you something that is partially irreducible. I think perhaps you simply wantabbrev
instead, in which case the proof will be rfl:abbrev f : ℕ → ℤ | 0 => 2 | 1 => 1 | 2 => 5 | (n + 1) => f n + 2 * f (n - 1) lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by induction' n with n ih · have : f 0 = 2 := rfl simp rw [show f 0 = 2 from rfl] · sorry
Or alternatively if you do want a def you can define some API for
f
.
Okay, that was unbeknownst to me. Where can I read about abbrev
and its differences with def
? I tried searching for it in Theorem Proving in Lean but haven't found anything. A link to some documentation would be of great help. Thank you, Julian.
Eric Wieser (Dec 01 2024 at 02:11):
I think you actual want
def f (n : Nat) : ℤ :=
match n with
| 0 => 2
| 1 => 1
| (n + 2) => f (n + 1) + 2 * f n
which by avoiding n-1
makes the function structurally recursive
Julian Berman (Dec 01 2024 at 02:12):
(https://lean-lang.org/doc/reference/latest/The-Lean-Language/Module-Contents/#The-Lean-Language-Reference--The-Lean-Language--Module-Contents--Commands-and-Declarations--Definition-Like-Commands FWIW is where I think will go over abbrev
vs def
but I see it isn't populated quite yet)
Divya Ranjan (Dec 01 2024 at 02:16):
Eric Wieser said:
I think you actual want
def f (n : Nat) : ℤ := match n with | 0 => 2 | 1 => 1 | (n + 2) => f (n + 1) + 2 * f n
which by avoiding
n-1
makes the function structurally recursive
Well, how does that differ? I could also define it using f (n)
directly but I'm not sure why recursively getting to f(n+1)
would differ from directly defining it.
Divya Ranjan (Dec 01 2024 at 02:27):
Julian Berman said:
I think this is saying
f 0
is indeed2
but the proof is notrfl
. I don't recall the technical details, it's something like thatdef
gives you something that is partially irreducible. I think perhaps you simply wantabbrev
instead, in which case the proof will be rfl:abbrev f : ℕ → ℤ | 0 => 2 | 1 => 1 | 2 => 5 | (n + 1) => f n + 2 * f (n - 1) lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by induction' n with n ih · have : f 0 = 2 := rfl simp rw [show f 0 = 2 from rfl] · sorry
Or alternatively if you do want a def you can define some API for
f
.
This brings the same problem when doing the succ
step of induction:
import Mathlib
--set_option diagnostics true
open Finset BigOperators
open Ring
open Nat
abbrev f : ℕ → ℤ
| 0 => 2
| 1 => 1
| 2 => 5
| (n + 1) => f n + 2 * f (n - 1)
/-
def f (n : Nat) : ℤ :=
match n with
| 0 => 2
| 1 => 1
| (n + 2) => f (n + 1) + 2 * f n
-/
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
· simp
rw [show f 0 = 2 from rfl]
· rw [show f (n + 1) = f (n) + 2 * f (n - 1) by rfl]
--have : f (n + 1) = f n + 2 * f (n - 1) := by ring
-- apply
sorry
This again gives:
tactic 'rfl' failed, the left-hand side
f (n + 1)
is not definitionally equal to the right-hand side
f n + 2 * f (n - 1)
Abraham Solomon (Dec 01 2024 at 02:49):
Divya Ranjan said:
Eric Wieser said:
I think you actual want
def f (n : Nat) : ℤ := match n with | 0 => 2 | 1 => 1 | (n + 2) => f (n + 1) + 2 * f n
which by avoiding
n-1
makes the function structurally recursiveWell, how does that differ? I could also define it using
f (n)
directly but I'm not sure why recursively getting tof(n+1)
would differ from directly defining it.
I think the difference is technical, but its about being able to make sense of (n-1):Nat for all n. (n+1), (n+2) is always easy to make sense of.
By default Nat has successor and 0, -1 isnt as easy to work with as n+1 and (n+1)+1 (being succ n and succ n (succ n))
Divya Ranjan (Dec 01 2024 at 02:52):
Abraham Solomon said:
Divya Ranjan said:
Eric Wieser said:
I think you actual want
def f (n : Nat) : ℤ := match n with | 0 => 2 | 1 => 1 | (n + 2) => f (n + 1) + 2 * f n
which by avoiding
n-1
makes the function structurally recursiveWell, how does that differ? I could also define it using
f (n)
directly but I'm not sure why recursively getting tof(n+1)
would differ from directly defining it.I think the difference is technical, but its about being able to make sense of (n-1):Nat for all n. (n+1), (n+2) is always easy to make sense of.
By default Nat has successor and 0, -1 isnt as easy to work with as n+1 and (n+1)+1 (being succ n and succ n (succ n))
Okay, that makes sense. Thank you.
Ruben Van de Velde (Dec 01 2024 at 07:32):
I think your issue is that n+1 could be 1 or 2 and those have different definitions
Divya Ranjan (Dec 01 2024 at 07:44):
Ruben Van de Velde said:
I think your issue is that n+1 could be 1 or 2 and those have different definitions
How so?
Divya Ranjan (Dec 01 2024 at 08:44):
Ruben Van de Velde said:
I think your issue is that n+1 could be 1 or 2 and those have different definitions
Because n could be 0 or 1?
Divya Ranjan (Dec 01 2024 at 08:54):
Should I be doing two base cases for 0 and 1?
Divya Ranjan (Dec 01 2024 at 08:56):
Divya Ranjan said:
Should I be doing two base cases for 0 and 1?
If so, how can I do that? Both the induction
and induction'
tactic seem to only create two implicit goals.
Kevin Buzzard (Dec 01 2024 at 09:42):
Can you ask your question in the form of a simple #mwe ?
Kevin Buzzard (Dec 01 2024 at 09:43):
The answer might be match n with
Divya Ranjan (Dec 01 2024 at 12:22):
Kevin Buzzard said:
Can you ask your question in the form of a simple #mwe ?
import Mathlib
open Finset BigOperators
open Ring
open Nat
abbrev f : ℕ → ℤ
| 0 => 2
| 1 => 1
| 2 => 5
| (n + 1) => f n + 2 * f (n - 1)
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
· simp
rw [show f 0 = 2 from rfl]
· -- rw [show f (n + 1) = f n + 2 * f (n - 1) by rfl]
sorry
Divya Ranjan (Dec 01 2024 at 12:25):
Kevin Buzzard said:
The answer might be
match n with
Where should I use it? After the opening induction statement?
Eric Wieser (Dec 01 2024 at 12:27):
In your example the commented show line is false, consider n = 0
Divya Ranjan (Dec 01 2024 at 12:30):
Eric Wieser said:
In your example the commented show line is false, consider
n = 0
That is impossible though, because that would require us to define f(-1)
which is outside of our scope, the proof is for all .
Eric Wieser (Dec 01 2024 at 12:31):
Are you aware of what #eval 1 - 2
gives?
Divya Ranjan (Dec 01 2024 at 12:33):
Ah, I didn't know Lean gives 0
to attempts of producing a negative number. Okay, should I use rcases
or cases
here to handle the n=0, being 0
and then proceed?
Divya Ranjan (Dec 01 2024 at 12:36):
But wait, that would lead to defining a contradictory version of f (1)
.
Ruben Van de Velde (Dec 01 2024 at 12:52):
So the first step I'd suggest is to make sure you have no overlapping patterns in your definition, so n + 3 instead of n + 1, and adjust the formula accordingly
Ruben Van de Velde (Dec 01 2024 at 12:54):
Also the 2 case is redundant, so you could have patterns 0, 1, and n + 2
Divya Ranjan (Dec 01 2024 at 12:57):
Ruben Van de Velde said:
Also the 2 case is redundant, so you could have patterns 0, 1, and n + 2
Like this?
import Mathlib
open Finset BigOperators
open Ring
open Nat
abbrev f : ℕ → ℤ
| 0 => 2
| 1 => 1
| (n + 2) => f (n + 1) + 2 * f n
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
· simp
rw [show f 0 = 2 from rfl]
· -- rw [show f (n + 1) = f n + 2 * f (n - 1) by rfl]
sorry
Divya Ranjan (Dec 01 2024 at 12:58):
Uncommenting the rw
gives the same error:
tactic 'rfl' failed, the left-hand side
f (n + 1)
is not definitionally equal to the right-hand side
f n + 2 * f (n - 1)
Eric Wieser (Dec 01 2024 at 13:19):
I think the first n - 1
should be n
, as it was in my post above
Eric Wieser (Dec 01 2024 at 13:19):
Also, you're still in trouble, because when n = 0
your commented line is still false
Eric Wieser (Dec 01 2024 at 13:19):
You need to handle the cases 0
, 1
, and n + 2
, not 0
and n + 1
Divya Ranjan (Dec 01 2024 at 13:21):
Eric Wieser said:
I think the first
n - 1
should ben
, as it was in my post above
Indeed, a typo.
Divya Ranjan (Dec 01 2024 at 13:22):
Eric Wieser said:
You need to handle the cases
0
,1
, andn + 2
, not0
andn + 1
But how? The code below only shows two goals: zero
and succ
:
import Mathlib
open Finset BigOperators
open Ring
open Nat
abbrev f : ℕ → ℤ
| 0 => 2
| 1 => 1
| (n + 2) => f (n + 1) + 2 * f n
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
. sorry
. sorry
Eric Wieser (Dec 01 2024 at 13:23):
You can even ask Lean to tell you that it is false:
import Mathlib
import Plausible
open Finset BigOperators
open Ring
open Nat
def f : ℕ → ℤ
| 0 => 2
| 1 => 1
| (n + 2) => f (n + 1) + 2 * f n
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
· simp [f]
· have : f (n + 1) = f n + 2 * f (n - 1) := by
plausible -- error, found counterexample
rw [this]
sorry
Divya Ranjan (Dec 01 2024 at 13:49):
Eric Wieser said:
You can even ask Lean to tell you that it is false:
import Mathlib import Plausible open Finset BigOperators open Ring open Nat def f : ℕ → ℤ | 0 => 2 | 1 => 1 | (n + 2) => f (n + 1) + 2 * f n lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by induction' n with n ih · simp [f] · have : f (n + 1) = f n + 2 * f (n - 1) := by plausible -- error, found counterexample rw [this] sorry
Apologies if this is something trivial, but I fail to understand this. How are we to show the n=0
case in the have
statement, you'd have to prove if f(1) = 6
which is contradictory. After the last simp
in the zero
case, Lean asks to prove f (1) = 6
:
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := by
induction' n with n ih
· simp [f]
· have : f (n + 1) = f n + 2 * f (n - 1) := by
· cases n with
| zero =>
simp
rw [show f 0 = 2 by rfl]
simp
| succ zero => sorry
rw [this]
rw [pow_add, pow_one, mul_two]
sorry
Eric Wieser (Dec 01 2024 at 14:00):
Divya Ranjan said:
How are we to show the
n=0
case in thehave
statement, you'd have to prove iff(1) = 6
which is contradictory.
We are not able to prove this; but plausible
gives an error message that tells you about this contradiction.
Eric Wieser (Dec 01 2024 at 14:00):
Lean is happy to continue the rest of the proof as though it were fine though, since it already told you that it was wrong the first time.
Eric Wieser (Dec 01 2024 at 14:00):
This is a feature, because it means Lean checks as much of your proof as possible, rather than stopping after the first error.
Divya Ranjan (Dec 01 2024 at 14:00):
So I should just put a plausible
and continue with the rest?
Eric Wieser (Dec 01 2024 at 14:01):
No! but you should use plausible
to check if your goals are false
Eric Wieser (Dec 01 2024 at 14:01):
And then if they are, rewrite your proof until your goal is not false!
Divya Ranjan (Dec 01 2024 at 14:01):
Okay, so that have
statement is not correct, and I should come up with something else..
Eric Wieser (Dec 01 2024 at 14:02):
Your example above with cases
above is close, but you should do the cases before the have
Eric Wieser (Dec 01 2024 at 14:02):
(you might also need to move the cases
before the induction
, I don't remember whether it matters)
Divya Ranjan (Dec 01 2024 at 14:03):
Eric Wieser said:
Your example above with
cases
above is close, but you should do the cases before the have
Okay, just so I don't continue being confused, the cases should be zero
, succ zero
and succ _
, right?
Eric Wieser (Dec 01 2024 at 14:13):
No, they should be zero
, succ zero
, and succ (succ _)
Eric Wieser (Dec 01 2024 at 14:13):
Aka 0
, 1
, and n + 2
Divya Ranjan (Dec 01 2024 at 14:13):
Understood, I was wondering that. Thanks, let me try.
Eric Wieser (Dec 01 2024 at 14:13):
Which as Kevin alludes to earlier, you can get more directly using match
instead of induction
/cases
. It would be a good exercise to write the proof once in each style
Divya Ranjan (Dec 01 2024 at 14:14):
Eric Wieser said:
Which as Kevin alludes to earlier, you can get more directly using
match
instead ofinduction
/cases
. It would be a good exercise to write the proof once in each style
You can do an inductive proof with just the match
tactic, without induction/induction'?
Ruben Van de Velde (Dec 01 2024 at 14:20):
Yeah, and instead of an explicit induction hypothesis, you use the name of the lemma itself
Kevin Buzzard (Dec 01 2024 at 14:47):
import Mathlib
open Finset BigOperators
open Ring
open Nat
def f : ℕ → ℤ
| 0 => 2
| 1 => 1
| (n + 2) => f (n + 1) + 2 * f n
lemma functional_equation {n : Nat} : f n = 2^n + (-1)^n := match n with
| 0 => by simp [f]
| 1 => by simp [f]
| (n + 2) => by
have hn : f n = 2^n + (-1)^n := @functional_equation n
have hnsucc : f (n + 1) = 2^(n + 1) + (-1)^(n + 1) := @functional_equation (n + 1)
simp [f] -- simplifies f (n + 2)
-- off you go
sorry
Divya Ranjan (Dec 01 2024 at 15:07):
Thanks a lot @Kevin Buzzard and @Eric Wieser , finally finished with the proof. And just to have better knowledge, I tried to do it with cases
/induction
but somehow couldn't figure out which statement comes first, if any of you could share a resource with examples, I'd be glad.
Kevin Buzzard (Dec 01 2024 at 15:12):
If you want to do bare induction then you will have to think hard about exactly what statement you want to prove by induction on n, remembering that in the d + 1
case you will only have access to a statement about d
, and in particular even if in your mental model you have "already proved the d-1 case", that's not how induction works in a theorem prover, you will not have access to it. So you would have to prove something like "for all n, f n = 2^n + (-1)^n and also f(n+1)=2^(n+1)+(-1)^(n+1)" by induction on n if you just used the induction
tactic without any bells and whistles. However there will be a trick where you can use something like induction n using Nat.two_step_induction
or whatever it's called, where you drop in the appropriate induction hypothesis which it uses instead of the usual one, and then you won't have to do this workaround.
Edward van de Meent (Dec 01 2024 at 18:24):
i think there already is a trick where instead of Nat.two_step_induction
you use f.induct
Edward van de Meent (Dec 01 2024 at 18:25):
(lean generates some usefull lemmas for structurally recursive functions)
Last updated: May 02 2025 at 03:31 UTC