I'm interested in a variation of the argument that stablishes:
"(∑ i=1..k . i) = k*(k+1) div 2"
We know this follows from a simple induction but the intution is a bit different. A way to see this formula is that if you sum the extremes of the sequence of numbers 1..k you get
1+k = 2 + (k-1) = ...
and then you just multiply the right number of times to get the complete sum.
I would like to reproduce this argument to show the following inequality:
"(∑n = 1..k - 1. cmod (f (int n))) ≤ 2 * (∑n ≤ k div 2. cmod (f (int n)))"
Here I know that cmod (f (int k - n)) = cmod (cnj (f n)) for every n.
Do you see an elegant way of proving this in Isabelle?
The trick to do this proof in an elegant way is to realise that ∑i=1..k. i is the same as ∑i=1..k. k + 1 - i and then adding that to the original sum so that the i cancels. This is a simple re-indexing argument:
lemma "(∑i=1..k. i :: nat) = k * (k + 1) div 2"
proof -
have "(∑i=1..k. i) = (∑i=1..k. k + 1 - i)"
by (rule sum.reindex_bij_witness[of _ "λi. k + 1 - i" "λi. k + 1 - i"]) auto
hence "2 * (∑i=1..k. i) = (∑i=1..k. i) + (∑i=1..k. k + 1 - i)"
by simp
also have "… = k * (k + 1)"
by (simp add: sum.distrib [symmetric])
finally show ?thesis by simp
qed
For the other thing you mentioned, I think that the best way to do this is to first split the sum into the elements less that k div 2 and the rest. Then you can reindex the second sum similarly to the one above. Then the inequality part comes in because you might have one extra leftover element ‘in the middle’ if k is odd and you have to throw that away.
Brief sketch of the important part of the proof:
lemma
assumes "⋀i. f i ≥ 0"
shows "(∑i=1..<k. f (i::nat) :: real) = T"
proof -
(* Separate summation domain into two disjoint parts *)
have "(∑i=1..<k. f i) = (∑i∈{1..k div 2}∪{k div 2<..<k}. f i)"
by (intro sum.cong) auto
(* Pull sum apart *)
also have "… = (∑i∈{1..k div 2}. f i) + (∑i∈{k div 2<..<k}. f i)"
by (subst sum.union_disjoint) auto
(* Reindex the second sum *)
also have "(∑i∈{k div 2<..<k}. f i) = (∑i∈{1..<k - k div 2}. f (k - i))"
by (rule sum.reindex_bij_witness[of _ "λi. k - i" "λi. k - i"]) auto
(* Throw away the element in the middle if k is odd *)
also have "… ≤ (∑i∈{1..k div 2}. f (k - i))"
using assms by (intro sum_mono2) auto
finally have "(∑i=1..<k. f i) ≤ (∑i=1..k div 2. f i + f (k - i))"
by (simp add: sum.distrib)
Figuring out how to do these sum manipulations idiomatically in Isabelle takes some experience. sum.reindex_bij_witness is a very useful rule (as you can see). Things like sum.mono_neutral_left/right can also help a lot.
Related
What am I doing wrong/ forgetting that this can't be automatically proven by isabelle:
lemma "sqrt 5 = (1 + sqrt 5) / 2 - (1 - sqrt 5) / 2"
apply auto
sorry
This is what I actually want to prove:
fun fib :: "nat ⇒ nat" where
"fib (Suc 0) = Suc 0"
| "fib (Suc (Suc 0)) = Suc 0"
| "fib (Suc n) = (fib (n - 1)) + (fib (n - 2))"
lemma "(n::nat) > 0 ⟹ fib n = (1 / sqrt 5) * (
(((1 + sqrt 5) / 2)^n) -
(((1 - sqrt 5) / 2)^n)
)"
proof(induction n)
case 0
then show ?case
by auto
next
case (Suc n)
then show ?case
proof(induction n)
case 0
then show ?case
apply(auto)
sorry
next
case (Suc n)
then show ?case
apply auto
sorry
qed
qed
Sledgehammer usually isn't good at doing heavy arithmetic rewriting, which is required here. Proof methods that use the simplifier, like auto and simp, are good at this. But you have to give them the right rules.
There are a number of theorem collections for this:
algebra_simps, which normalises a term w.r.t. associativity, commutativity, and distributivity (essentially sorting everything in ascending order and multiplying out)
field_simps, which additionally cross-multiplies fractions. But caution: the denominator must be provably non-zero. If the simplifier cannot show that, it will not cross-multiply. If the denominator is a product of several factors, it may multiply out the product first and then the denominator is so ugly that it cannot show non-zeroness anymore.
divide_simps is like field_simps except that it does not use distributivity, and it always cross-multiplies by introducing a case distinction for the case that the denominator is zero. (In Isabelle/HOL, x / 0 = 0 holds by definition.)
Another rule you will need here is power2_eq_square to rewrite the square into a multiplication. Then you get:
lemma "sqrt 5 = (1 + sqrt 5) / 2 - ((1 - sqrt 5) / 2)^2"
apply (auto simp: field_simps power2_eq_square)
proof (prove)
goal (1 subgoal):
1. False
So something is not quite right here. As NieDzejkob pointed out in the comment, the left-hand side should be sqrt 5 - 1. With that, the proof goes through.
By the way, in case you're not aware, Fibonacci numbers are in the Isabelle/HOL standard library: https://isabelle.in.tum.de/library/HOL/HOL-Number_Theory/Fib.html
While doing some basic algebra, I frequently arrive at a subgoal of the following type (sometimes with a finite sum, sometimes with a finite product).
lemma foo:
fixes N :: nat
fixes a :: "nat ⇒ nat"
shows "(a 0) = (∑x = 0..N. (if x = 0 then 1 else 0) * (a x))"
This seems pretty obvious to me, but neither auto nor auto cong: sum.cong split: if_splits can handle this. What's more, sledgehammer also surrenders when called on this lemma. How can one efficiently work with finite sums and products containing if-then-else in general, and how to approach this case in particular?
My favourite way to do these things (because it is very general) is to use the rules sum.mono_neutral_left and sum.mono_neutral_cong_left and the corresponding right versions (and analogously for products). The rule sum.mono_neutral_right lets you drop arbitrarily many summands if they are all zero:
finite T ⟹ S ⊆ T ⟹ ∀i∈T - S. g i = 0
⟹ sum g T = sum g S
The cong rule additionally allows you to modify the summation function on the now smaller set:
finite T ⟹ S ⊆ T ⟹ ∀i∈T - S. g i = 0 ⟹ (⋀x. x ∈ S ⟹ g x = h x)
⟹ sum g T = sum h S
With those, it looks like this:
lemma foo:
fixes N :: nat and a :: "nat ⇒ nat"
shows "a 0 = (∑x = 0..N. (if x = 0 then 1 else 0) * a x)"
proof -
have "(∑x = 0..N. (if x = 0 then 1 else 0) * a x) = (∑x ∈ {0}. a x)"
by (intro sum.mono_neutral_cong_right) auto
also have "… = a 0"
by simp
finally show ?thesis ..
qed
Assuming the left-hand side could use an arbitrary value between 0 and N, what about adding a more general lemma
lemma bar:
fixes N :: nat
fixes a :: "nat ⇒ nat"
assumes
"M ≤ N"
shows "a M = (∑x = 0..N. (if x = M then 1 else 0) * (a x))"
using assms by (induction N) force+
and solving the original one with using bar by blast?
I was going through chapter 5 (Isar) and I tried doing the structural induction proof for "Σ{0..n::nat} = n*(n+1) div 2" but it fails:
lemma "Σ{0..n::nat} = n*(n+1) div 2"
proof (induction n)
show "Σ{0..0::nat} = 0*(0+1) div 2" by simp
next
fix n
assume "Σ {0..n} = n * (n + 1) div 2"
thus "Σ {0..Suc n} = Suc n * (Suc n + 1) div 2" by simp
qed
it says:
show Σ {0..0} = 0 * (0 + 1) div 2
Successful attempt to solve goal by exported rule:
Σ {0..0} = 0 * (0 + 1) div 2
proof (state)
this:
Σ {0..0} = 0 * (0 + 1) div 2
goal (1 subgoal):
1. ⋀n. Σ {0..n} = n * (n + 1) div 2 ⟹ Σ {0..Suc n} = Suc n * (Suc n + 1) div 2
Failed to finish proof⌂:
goal (1 subgoal):
1. Σ {0} = 0
I don't know why. Sledgehammer didn't solve it either. I did try blast , auto etc but I knew they'd fail since sledgehammer has suggested those to me before but it was worth the try?
I tried doing the apply style to see what going on:
lemma "Σ{0..n::nat} = n*(n+1) div 2"
apply (induction n)
apply simp
apply simp
same error:
proof (prove)
goal (2 subgoals):
1. Σ {0} = 0
2. ⋀n. Σ {0..n} = n * (n + 1) div 2 ⟹ Σ {0..Suc n} = Suc n * (Suc n + 1) div 2
Failed to apply proof method⌂:
goal (2 subgoals):
1. Σ {0} = 0
2. ⋀n. Σ {0..n} = n * (n + 1) div 2 ⟹ Σ {0..Suc n} = Suc n * (Suc n + 1) div 2
why is this not working? Is there something wrong with my installation of Isabelle?
I also tried the proof on a file without anything and it also failed so it's not any of my earlier definitions (I assume with high probability).
Seems that going to the bottom right where I can manually insert symbols is a bad idea. It inserted the symbol sigma instead of Sum. I fixed it by doing \<Sum> (in reality I auto completed with tab). Proof works now:
lemma "∑{0..n::nat} = n*(n+1) div 2"
apply (induction n)
apply simp
by simp
There seems to be something about inductive predicates I don't understand since I keep getting issues with them. My most recent struggle is to understand case analysis with inductively defined predicates on ev from chapter 5 of the concrete semantics books.
Assume I am proving
lemma
shows "ev n ⟹ ev (n - 2)"
I've tried to start the proof immediately in Isabelle/HOL but it complains when I try or gives weird goals:
lemma
shows "ev n ⟹ ev (n - 2)"
proof (cases)
which shows:
proof (state)
goal (2 subgoals):
1. ⟦ev n; ?P⟧ ⟹ ev (n - 2)
2. ⟦ev n; ¬ ?P⟧ ⟹ ev (n - 2)
which is not what I expected.
When I pass n to cases we instead induct on the definition of natural numbers (note other times it does the induction on ev correctly, see later example):
lemma
shows "ev n ⟹ ev (n - 2)"
proof (cases n)
gives:
proof (state)
goal (2 subgoals):
1. ⟦ev n; n = 0⟧ ⟹ ev (n - 2)
2. ⋀nat. ⟦ev n; n = Suc nat⟧ ⟹ ev (n - 2)
which is not what I expected. Note however that the following DOES work (i.e. it inducts on ev not on natural numbers) even with n as a parameter:
lemma
shows "ev n ⟹ ev (n - 2)"
proof -
assume 0: "ev n"
from this show "ev (n - 2)"
proof (cases n)
I realize that there must be some magic about assuming ev n first and then stating to show ev (n-2) otherwise the error wouldn't occur.
I understand the idea of rule inversion (of arriving at a given fact to be proven in reverse, by analysis the cases that could have lead to it). For the "even" predicate the rule inversion is:
ev n ==> n = 0 ∨ (∃k. n = Suc (Suc k) ∧ ev k)
which makes sense based on the inductively defined predicate:
inductive ev :: "nat ⇒ bool" where
ev0: "ev 0" |
evSS: "ev n ⟹ ev (Suc (Suc n))"
but I don't understand. Why wouldn't directly doing the cases work? or why is this syntax invalid:
proof (cases ev)
or this:
proof (cases ev.case)
etc.
I think the crux is that at the heart I don't know when dealing with inductively define predicates if this induction is applied to the goal or the assumption, but from the writing on of the textbook:
The name rule inversion emphasizes that we are reasoning backwards: by which rules could some given fact have been proved?
I'd assume it's applying rule inversion to the goals since it says "by which rules could some given fact have been proved".
In addition, this example ev ==> ev (n-2) froom the book does not help because both the premise and conclusion have ev involved.
How is case analysis with rule inversion really working and why do we need to assume things first for Isabelle to give sensible goal for case analysis?
Not sure I understand the entire question but this:
lemma
shows "ev n ⟹ ev (n - 2)"
proof (cases)
Gives you:
proof (state)
goal (2 subgoals):
1. ⟦ev n; ?P⟧ ⟹ ev (n - 2)
2. ⟦ev n; ¬ ?P⟧ ⟹ ev (n - 2)
because Isabelle is splitting on the truth of ev n, i.e. either true or false. I believe the syntax you are looking for is:
proof (cases rule: ev.cases)
Which is how you tell Isabelle explicitly what rule it should use for a proof by cases.
The way to do it is as the answer Ben Sheffield said:
proof (cases rule: ev.cases)
I also noticed that:
apply (rule ev.cases)
works, but I think it would be helpful to go through a small example to see the cases explicitly outlined:
Consider:
lemma "ev n ⟹ ev (n - 2)"
first inspect it's cases theorem:
thm ev.cases
⟦ev ?a; ?a = 0 ⟹ ?P; ⋀n. ⟦?a = Suc (Suc n); ev n⟧ ⟹ ?P⟧ ⟹ ?P
then it unifies the goal and introduces the new goals with the original assumptions and all the assumption for the cases. That is why there is a ev n in all of them.
apply (rule ev.cases)
has goals:
proof (prove)
goal (3 subgoals):
1. ev n ⟹ ev ?a
2. ⟦ev n; ?a = 0⟧ ⟹ ev (n - 2)
3. ⋀na. ⟦ev n; ?a = Suc (Suc na); ev na⟧ ⟹ ev (n - 2)
and you can do simp and proceed the proof as normal.
I have the following lemma to show the derivative of f at x is D.
lemma lm1:
assumes "(∀h. (f (x + h) - f x) = D*h)"
shows "DERIV f x :> D"
proof cases
assume notzero: "∀h. h ≠ 0"
have cs1: "(λh. (f (x + h) - f x) / h) -- 0 --> D" using assms notzero by auto
from this DERIV_def show ?thesis by auto
From the assumptions, I can easily prove the lemma by taking the limit then using DERIV_def. For this I have to assume that h ≠ 0. Continuing with the proof by cases I have to show that even when h = 0, the goal is true, however this can't be done when h = 0 as the assumption becomes 0 = 0. The lemma becomes trivial.
Is there a way I can prove the goal, which is this case is that f has derivative D at x, without the additional assumption that h ≠ 0?
edit: After further research, I came across the use of elimination rules in Isabelle which may be helpful. Also, I understand that the lemma is correct as the if the function is continuous, then the derivative at 0 also exists.
I have been searching for the correct use and implementation of the the above information. How can I improve my search, and where should I be looking?