Proving simple theorem about cases mod 10 - isabelle

I'd like to prove the following lemma:
lemma mod10_cases:
"P 0 ⟹ P 1 ⟹ P 2 ⟹ P 3 ⟹ P 4 ⟹ P 5 ⟹ P 6 ⟹ P 7 ⟹ P 8 ⟹ P 9 ⟹ P (n mod 10)"
but am finding it surprisingly tricky. The lemma feels straightforward; it just says that in order to prove a property P holds of every number modulo 10, I just need to check that it holds of 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9.
(Wider picture: I want to use my lemma to prove theorems of the form foo(n mod 10), which I intend to start by saying apply (cases rule: mod10_cases).)

My usual trick is to first show n mod 10 ∈ {..<10} (which is trivially proven by simp) and then unfold {..<10} to {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, which can be done by feeding the right rules to the simplifier:
lemma mod10_induct [case_names 0 1 2 3 4 5 6 7 8 9]:
fixes n :: nat
assumes "P 0" "P 1" "P 2" "P 3" "P 4" "P 5" "P 6" "P 7" "P 8" "P 9"
shows "P (n mod 10)"
proof -
have "n mod 10 ∈ {..<10}"
by simp
also have "{..<10} = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9 :: nat}"
by (simp add: lessThan_nat_numeral lessThan_Suc insert_commute)
finally show ?thesis using assms
by fastforce
qed
Note that the way you wrote your theorem, it has the shape of an induction rule, not a case-splitting rule. A case-splitting rule would look more like this:
lemma mod10_cases [case_names 0 1 2 3 4 5 6 7 8 9]:
fixes n :: nat
obtains "n mod 10 = 0" | "n mod 10 = 1" | "n mod 10 = 2" | "n mod 10 = 3" | "n mod 10 = 4" |
"n mod 10 = 5" | "n mod 10 = 6" | "n mod 10 = 7" | "n mod 10 = 8" | "n mod 10 = 9"
proof -
have "n mod 10 ∈ {..<10}"
by simp
also have "{..<10} = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9 :: nat}"
by (simp add: lessThan_nat_numeral lessThan_Suc insert_commute)
finally show ?thesis using that
by fast
qed
If the obtains here confuses you, the final theorem you get from this looks like this:
theorem mod10_cases:
(?n mod 10 = 0 ⟹ ?thesis) ⟹
(?n mod 10 = 1 ⟹ ?thesis) ⟹
(?n mod 10 = 2 ⟹ ?thesis) ⟹
(?n mod 10 = 3 ⟹ ?thesis) ⟹
(?n mod 10 = 4 ⟹ ?thesis) ⟹
(?n mod 10 = 5 ⟹ ?thesis) ⟹
(?n mod 10 = 6 ⟹ ?thesis) ⟹
(?n mod 10 = 7 ⟹ ?thesis) ⟹
(?n mod 10 = 8 ⟹ ?thesis) ⟹
(?n mod 10 = 9 ⟹ ?thesis) ⟹ ?thesis
You can then use your rules like this:
lemma "P (n mod 10 :: nat)"
apply (induction n rule: mod10_induct)
oops
lemma "P (n mod 10 :: nat)"
apply (cases n rule: mod10_cases)
oops
In most circumstances, however, I would not derive a separate rule for this as you did to begin with – I would simply do the unfolding of {..<10} at the place where I need it instead, since it only takes about 4 lines, and then feed the fact that n mod 10 ∈ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} to my actual goal and hit it with auto.

Related

Rewrite with implications in Isabelle

I am looking for a method to do rewriting, but with implications instead of equalities.
For example I know that x = 3 ∧ y = 4 implies Q x y and now I want to replace a positive occurrence of Q x y in my current subgoal with x = 3 ∧ y = 4.
Is there an existing method in Isabelle to do this?
For example I would like to do somthing like this (where implication_subst is the name of the method I am looking for):
lemma
assumes a1: "⋀x y. x = 3 ∧ y = 4 ⟹ Q x y"
shows "(∃x y. A x ∧ Q x y ∧ B y)"
proof (implication_subst a1)
show "∃x y. A x ∧ (x = 3 ∧ y = 4) ∧ B y"
sorry
qed
Below is my (incomplete) attempt to implement such a method using Eisbach, maybe this gives a better idea of what I am looking for:
named_theorems pos_cong
lemma implication_subst_exists[pos_cong]:
assumes "⋀x. P x ⟹ Q x"
and "∃x. P x"
shows "∃x. Q x"
using assms by blast
lemma implication_subst_conjl[pos_cong]:
assumes "P ⟹ Q"
and "P ∧ A"
shows "Q ∧ A"
using assms by blast
lemma implication_subst_conjr[pos_cong]:
assumes "P ⟹ Q"
and "A ∧ P"
shows "A ∧ Q"
using assms by blast
lemma implication_subst_neg[pos_cong]:
assumes "P ⟹ Q"
and "P"
shows "¬¬Q"
using assms by auto
lemma implication_subst_impl[pos_cong]:
assumes "P ⟹ ¬Q"
and "¬P ⟶ A"
shows "Q ⟶ A"
using assms by auto
lemma implication_subst_impr[pos_cong]:
assumes "P ⟹ Q"
and "A ⟶ P"
shows "A ⟶ Q"
using assms by auto
lemma implication_subst_neg_disj_l[pos_cong]:
assumes "P ⟹ ¬Q"
and "¬(¬P ∨ A)"
shows "¬(Q ∨ A)"
using assms by auto
lemma implication_subst_neg_disj_r[pos_cong]:
assumes "P ⟹ ¬Q"
and "¬(A ∨ ¬P)"
shows "¬(A ∨ Q)"
using assms by auto
method implication_subst_h uses r declares pos_cong = (
rule r
| (rule pos_cong, implication_subst_h r: r, assumption))
method implication_subst uses r declares pos_cong =
(implication_subst_h r: r pos_cong: pos_cong, (unfold not_not)?)
lemma example1:
assumes a1: "⋀x y. x = 3 ∧ y = 4 ⟹ Q x y"
shows "∃x y. A x ∧ Q x y ∧ B y"
proof (implication_subst r: a1)
show "∃x y. A x ∧ (x = 3 ∧ y = 4) ∧ B y"
sorry
qed
lemma example2:
assumes a1: "⋀x y. x = 3 ∧ y = 4 ⟹ Q x y"
shows "(∃x y. ¬(¬A x ∨ ¬Q x y ∨ ¬B y))"
proof (implication_subst r: a1)
show "∃x y. ¬ (¬ A x ∨ ¬ (x = 3 ∧ y = 4) ∨ ¬ B y)"
sorry
qed

How to prove elimination rules using Isar?

Here is a simple theory:
datatype t1 = A | B | C
datatype t2 = D | E t1 | F | G
inductive R where
"R A B"
| "R B C"
inductive_cases [elim]: "R x B" "R x A" "R x C"
inductive S where
"S D (E _)"
| "R x y ⟹ S (E x) (E y)"
inductive_cases [elim]: "S x D" "S x (E y)"
I can prove lemma elim using two helper lemmas:
lemma tranclp_S_x_E:
"S⇧+⇧+ x (E y) ⟹ x = D ∨ (∃z. x = E z)"
by (induct rule: converse_tranclp_induct; auto)
(* Let's assume that it's proven *)
lemma reflect_tranclp_E:
"S⇧+⇧+ (E x) (E y) ⟹ R⇧+⇧+ x y"
sorry
lemma elim:
"S⇧+⇧+ x (E y) ⟹
(x = D ⟹ P) ⟹ (⋀z. x = E z ⟹ R⇧+⇧+ z y ⟹ P) ⟹ P"
using reflect_tranclp_E tranclp_S_x_E by blast
I need to prove elim using Isar:
lemma elim:
assumes "S⇧+⇧+ x (E y)"
shows "(x = D ⟹ P) ⟹ (⋀z. x = E z ⟹ R⇧+⇧+ z y ⟹ P) ⟹ P"
proof -
assume "S⇧+⇧+ x (E y)"
then obtain z where "x = D ∨ x = E z"
by (induct rule: converse_tranclp_induct; auto)
also have "S⇧+⇧+ (E z) (E y) ⟹ R⇧+⇧+ z y"
sorry
finally show ?thesis
But I get the following errors:
No matching trans rules for calculation:
x = D ∨ x = E z
S⇧+⇧+ (E z) (E y) ⟹ R⇧+⇧+ z y
Failed to refine any pending goal
Local statement fails to refine any pending goal
Failed attempt to solve goal by exported rule:
(S⇧+⇧+ x (E y)) ⟹ P
How to fix them?
I guess that this lemma could have a simpler proof. But I need to prove it in two steps:
Show the possible values of x
Show that E reflects transitive closure
I think also that this lemma could be proven by cases on x. But my real data types have too many cases. So, it's not a preferred solution.
This variant seems to work:
lemma elim:
assumes "S⇧+⇧+ x (E y)"
and "x = D ⟹ P"
and "⋀z. x = E z ⟹ R⇧+⇧+ z y ⟹ P"
shows "P"
proof -
have "S⇧+⇧+ x (E y)" by (simp add: assms(1))
then obtain z where "x = D ∨ x = E z"
by (induct rule: converse_tranclp_induct; auto)
moreover
have "S⇧+⇧+ (E z) (E y) ⟹ R⇧+⇧+ z y"
sorry
ultimately show ?thesis
using assms by auto
qed
Assumptions should be separated from the goal.
As a first statement I shoud use have instead of assume. It's not a new assumption, just the existing one.
Instead of finally I should use ultimately. It seems that the later one has a simpler application logic.

Inductive numerical proof: for k>= 10 it is true that 2^k > k^3

I am trying to prove something, that I thought would be relatively simple.
(k≥10 ⟹ 2^(k::nat) > (k::nat)^3)
However, I am getting stuck and do not know how to progress.
lemma "(k≥10 ⟹ 2^(k::nat) > (k::nat)^3)"
proof (induction k)
case 0
then show ?case by simp
next
case (Suc k)
assume "10 ≤ k ⟹ k ^ 3 < 2 ^ k"
assume "10 ≤ Suc k"
show "(Suc k) ^ 3 < 2 ^( Suc k)"
apply (simp add: algebra_simps)
then show ?case sorry
qed
A good induction rule can save a lot of hassle. You want to start induction with 10, not 0, so maybe there is a better induction rule? Using
find_theorems name:ind name:Nat
I find this one
Nat.dec_induct: ?i ≤ ?j ⟹ ?P ?i ⟹ (⋀n. ?i ≤ n ⟹ n < ?j ⟹ ?P n ⟹ ?P (Suc n)) ⟹ ?P ?j
From now on it is simple equational reasoning, and I was able to follow the proof in this math stackexchange answer step by step:
lemma
assumes "k≥10"
shows "2^(k::nat) > (k::nat)^3"
using assms
proof(induction rule: Nat.dec_induct)
case base show ?case by simp
next
case (step n)
note power2_eq_square[simp] power3_eq_cube[simp] ring_distribs[simp]
have "Suc n ^ 3 = 1 + 3*n + 3*n^2 + n^3" by simp
also have "… < 51*n + 3*n + 3*n^2 + n^3" using `n ≥ 10` by simp
also have "… = 54*n + 3*n^2 + n^3" by simp
also have "… < 6*n^2 + 3*n^2 + n^3" using `n ≥ 10` by simp
also have "… = 9*n^2 + n^3" by simp
also have "… < n^3 + n^3" using `n ≥ 10` by simp
also have "… = 2 * n^3" by simp
also have "… < 2 * 2^n" using `n ^ 3 < 2 ^ n` by simp
also have "… = 2^(Suc n)" by simp
finally show ?case.
qed
For algebraic proofs, the trick is to exactly identify the simplification steps and then find_theorems to search the relevant rules. Algebraic simp set doesn't contain aggressive simplifaction rules, probably because oversimplification might leave the goals unprovable. (Someone can add more reasons)
In this particular case, you have to assume 10 ≤ k for induction hypothesis to work. You can easily do that by case_tac "10 ≤ k", and then rewriting by monoid_mult_class.power3_eq_cube and le_Suc_ex. For ¬ 10 ≤ k, you can prove the goal using not_less_eq_eq.

Proving the cardinality of a more involved set

Supposing I have a set involving three conjunctions {k::nat. 2<k ∧ k ≤ 7 ∧ gcd 3 k = 2}.
How can I prove in Isabelle that the cardinality of this set is 1 ? (Namely only k=6 has gcd 3 6 = 2.) I.e., how can I prove lemma a_set : "card {k::nat. 2<k ∧ k ≤ 7 ∧ gcd 3 k = 2} = 1" ?
Using sledgehammer (or try) again doesn't yield results - I find it very difficult to find what exactly I need to give the proof methods to make them able to to the proof. (Even removing, e.g. gcd 3 k = 2, doesn't make it amenable to auto or sledgehammer.)
Your proposition is incorrect. The set you described is actually empty, as gcd 3 6 = 3. Sledgehammer can prove that the cardinality is zero without problems, although the resulting proof is again a bit ugly, as is often the case with Sledgehammer proofs:
lemma "card {k::nat. 2<k ∧ k ≤ 7 ∧ gcd 3 k = 2} = 0"
by (metis (mono_tags, lifting) card.empty coprime_Suc_nat
empty_Collect_eq eval_nat_numeral(3) gcd_nat.left_idem
numeral_One numeral_eq_iff semiring_norm(85))
Let's do it by hand, just to illustrate how to do it. These sorts of proofs do tend to get a little ugly, especially when you don't know the system well.
lemma "{k::nat. 2<k ∧ k ≤ 7 ∧ gcd 3 k = 2} = {}"
proof safe
fix x :: nat
assume "x > 2" "x ≤ 7" "gcd 3 x = 2"
from ‹x > 2› and ‹x ≤ 7› have "x = 3 ∨ x = 4 ∨ x = 5 ∨ x = 6 ∨ x = 7" by auto
with ‹gcd 3 x = 2› show "x ∈ {}" by (auto simp: gcd_non_0_nat)
qed
Another, much simpler way (but also perhaps more dubious one) would be to use eval. This uses the code generator as an oracle, i.e. it compiles the expression to ML code, compiles it, runs it, looks if the result is True, and then accepts this as a theorem without going through the Isabelle kernel like for normal proofs. One should think twice before using this, in my opinion, but for toy examples, it is perfectly all right:
lemma "card {k::nat. 2<k ∧ k ≤ 7 ∧ gcd 3 k = 2} = 0"
proof -
have "{k::nat. 2<k ∧ k ≤ 7 ∧ gcd 3 k = 2} = Set.filter (λk. gcd 3 k = 2) {2<..7}"
by (simp add: Set.filter_def)
also have "card … = 0" by eval
finally show ?thesis .
qed
Note that I had to massage the set a bit first (use Set.filter instead of the set comprehension) in order for eval to accept it. (Code generation can be a bit tricky)
UPDATE:
For the other statement from the comments, the proof has to look like this:
lemma "{k::nat. 0<k ∧ k ≤ 5 ∧ gcd 5 k = 1} = {1,2,3,4}"
proof (intro equalityI subsetI)
fix x :: nat
assume x: "x ∈ {k. 0 < k ∧ k ≤ 5 ∧ coprime 5 k}"
from x have "x = 1 ∨ x = 2 ∨ x = 3 ∨ x = 4 ∨ x = 5" by auto
with x show "x ∈ {1,2,3,4}" by (auto simp: gcd_non_0_nat)
qed (auto simp: gcd_non_0_nat)
The reason why this looks so different is because the right-hand side of the goal is no longer simply {}, so safe behaves differently and generates a pretty complicated mess of subgoals (just look at the proof state after the proof safe). With intro equalityI subsetI, we essentially just say that we want to prove that A = B by proving a ∈ A ⟹ a ∈ B and the other way round for arbitrary a. This is probably more robust than safe.

Induction on Recursive Function with a twist

I'm trying to proof the theorem that if n > 0 then g n b = True (see below). This is the case, because g (Suc n) b only ever calls g 0 True. Unfortunately, I don't have that fact in my induction when I try to proof g 0 b. How can I finish the proof (what do I have to replace the sorry with)?
fun g :: "nat ⇒ bool ⇒ bool" where
"g (Suc n) b = g n True" |
"g 0 b = b"
theorem
fixes n::nat and b::bool
assumes "n > 0"
shows "g n b"
proof (induct n b rule: g.induct)
fix n
fix b
assume "g n True"
thus "g (Suc n) b" by (metis g.simps(1))
next
fix b
show "g 0 b" sorry
qed
You forgot to use the assumption n > 0 in your induction.
E.g., you may write
theorem
fixes n::nat and b::bool
assumes "n > 0"
shows "g n b"
using assms (* this is important *)
proof (induct n b rule: g.induct)
case (1 n b)
thus ?case by (cases n) auto
next
case (2 b)
thus ?case by auto
qed
Alternatively you may immediately start your theorem like this
and shorten it further:
theorem "n > 0 ==> g n b"
proof (induct n b rule: g.induct)
case (1 n b)
thus ?case by (cases n) auto
qed auto

Resources