Simplifier doesn't work with constants greater than 10? - isabelle

Why does the Isabelle simplifier refuse to prove the following lemma?
lemma "n ≠ counter ⟹
n ≠ Suc counter ⟹
n ≠ Suc (Suc counter) ⟹
n ≠ counter + 3 ⟹
n ≠ counter + 4 ⟹
n ≠ counter + 5 ⟹
n ≠ counter + 6 ⟹
n ≠ counter + 7 ⟹
n ≠ counter + 8 ⟹
n ≠ counter + 9 ⟹
counter ≤ n ⟹ n < counter + 10 ⟹ False"
by simp
Although it proves a slightly smaller lemma
lemma "n ≠ counter ⟹
n ≠ Suc counter ⟹
n ≠ Suc (Suc counter) ⟹
n ≠ counter + 3 ⟹
n ≠ counter + 4 ⟹
n ≠ counter + 5 ⟹
n ≠ counter + 6 ⟹
n ≠ counter + 7 ⟹
n ≠ counter + 8 ⟹
counter ≤ n ⟹ n < counter + 9 ⟹ False"
by simp
It seems to me that the constant 10 is too large for the simplifier. Is there an option increasing the maximal processed constant or another workaround to prove the first lemma?

To answer for the workaround, you can use the "arith" or the "presburger" method:
lemma "n ≠ counter ⟹
n ≠ Suc counter ⟹
n ≠ Suc (Suc counter) ⟹
n ≠ counter + 3 ⟹
n ≠ counter + 4 ⟹
n ≠ counter + 5 ⟹
n ≠ counter + 6 ⟹
n ≠ counter + 7 ⟹
n ≠ counter + 8 ⟹
n ≠ counter + 9 ⟹
counter ≤ n ⟹ n < counter + 10 ⟹ False"
by presburger (* or by arith *)
arith also works but raises a warning (linarith failed) in this case, while this warning does not appear for the smaller lemma. There should be a correlation somewhere but I don't know exactly why the simp method fails though...
You could have discovered this by using the commands "try" or "try0" that try several provers and methods.

Thanks for the other answer for showing that arith can be used to prove the theorem, because arith is not one of the try0 methods.
By all appearances, this message from linarith_trace gives you the answer:
neq_limit exceeded (current value is 9), ignoring all inequalities
The value 10 comes into play not because you're using the constant 10, but because you're using 10 inequalities, as I show below. As usual, because I can't verify with someone what seems to be obvious (from looking at source files), I could be wrong.
I grepped on neq_limit in Isabelle2013-2/src and it shows up in two files. There's also an applicable example theory:
src/Provers/Arith/fast_lin_arith.ML
src/HOL/Tools/lin_arith.ML
src/HOL/ex/Arith_Examples.thy
Below, I quote from the files above, and I show, using [[simp_trace, linarith_trace]], whether I use apply(simp only:) or apply(arith), there's a lot of interaction between simp and various linear arithmetic methods.
I deleted my first answer. If anyone wants to see what I had to say about (simp only:), and other things about trying to track down things, you can click on edit somewhere below to see the old entries.
As shown below, whether I use apply(simp only:) or apply(arith), I get the neq_limit exceeded trace message above.
I speculate that the difference is between fast_lin_arith.ML and lin_arith.ML. In fast_lin_arith.ML, there is a check, at line 788, that's not in lin_arith.ML.
val split_neq = count is_neq (map (LA_Data.decomp ctxt) Hs') <= neq_limit
in
if split_neq then ()
else
trace_msg ctxt ("neq_limit exceeded (current value is " ^
This is related to a comment in that file:
(*the limit on the number of ~= allowed; because each ~= is split
into two cases, this can lead to an explosion*)
val neq_limit: int Config.T
In general, there's a lot of interaction between simp and the linear arithmetic automatic proof methods, as shown by a comment in Arith_Examples.thy.
The #{text arith} method combines them both, and tries other methods
(e.g.~#{text presburger}) as well. This is the one that you should use
in your proofs!
Now, I get rid of one not equal in your counter + 10 lemma (because it's redundant), which allows simp to prove it.
(*_SRC.0_*)
(*If we get rid of one inequality, and change the <= to <, it works.*)
lemma "(*n ≠ counter ==> *)
counter < n ==>
n ≠ Suc counter ==>
n ≠ Suc (Suc counter) ==>
n ≠ counter + 3 ==>
n ≠ counter + 4 ==>
n ≠ counter + 5 ==>
n ≠ counter + 6 ==>
n ≠ counter + 7 ==>
n ≠ counter + 8 ==>
n ≠ counter + 9 ==>
n < counter + 10 ==> False"
by(simp)
I show here that there's interaction between simp and a linear arithmetic tactic. (The comment in Arith_Examples.thy explains there are variations.)
(*_SRC.1_*)
lemma "
n ≠ counter ==>
n ≠ Suc counter ==>
n ≠ Suc (Suc counter) ==>
counter ≤ n ==>
n < counter + 3 ==> False"
using[[simp_trace, linarith_trace]]
(*Click "1000" at the bottom of the output panel to get it to finish.
The trace shows that `arith` is called very soon, but that there's also
a lot of simp rule rewriting, such as with the rule `Groups.add_ac_2`.*)
apply(simp only:)
done
I show that you get the same trace message about neq_limit with both apply(simp only:) and apply(arith), but one proof goes through, and one doesn't. Apparently, simp uses one form of a linear arithmetic tactic, and arith uses another.
(*_SRC.2_*)
lemma "n ≠ counter ==>
n ≠ Suc counter ==>
n ≠ Suc (Suc counter) ==>
n ≠ counter + 3 ==>
n ≠ counter + 4 ==>
n ≠ counter + 5 ==>
n ≠ counter + 6 ==>
n ≠ counter + 7 ==>
n ≠ counter + 8 ==>
n ≠ counter + 9 ==>
counter ≤ n ==>
n < counter + 10 ==> False"
using[[linarith_trace]]
apply(simp only:)
(*Gets the following error message, and it goes no further:
Trying to refute subgoal 1...
neq_limit exceeded (current value is 9), ignoring all inequalities
*)
oops
(*_SRC.3_*)
lemma "n ≠ counter ==>
n ≠ Suc counter ==>
n ≠ Suc (Suc counter) ==>
n ≠ counter + 3 ==>
n ≠ counter + 4 ==>
n ≠ counter + 5 ==>
n ≠ counter + 6 ==>
n ≠ counter + 7 ==>
n ≠ counter + 8 ==>
n ≠ counter + 9 ==>
counter ≤ n ==>
n < counter + 10 ==> False"
using[[simp_trace, linarith_trace]]
apply(arith)
(*Same complaint about "9", but the proof will go through if there's no trace:
Trying to refute subgoal 1...
neq_limit exceeded (current value is 9), ignoring all inequalities
*)
oops

Related

How do I prove a subgoal with bound variables with assumptions that have schematic variable?

I am an isabelle noob. I have a sublocale that gives me subgoals that have bound variables. The subgoals are exact copies of assumptions I have inside some other locales. When I instantiate them, they can only be done with free variables. How do I work around this issue?
Given here are my subgoals
1. ⋀n. plus n zero = n
2. ⋀n m. plus n (suc m) = suc (plus n m)
3. ⋀n. times n zero = zero
4. ⋀n m. times n (suc m) = plus (times n m) n
5. ⋀x. (zero = zero ∨ (∃m. suc m = zero)) ∧
(x = zero ∨ (∃m. suc m = x) ⟶
suc x = zero ∨ (∃m. suc m = suc x)) ⟶
(∀x. x = zero ∨ (∃m. suc m = x))
Here is an (just a few) of the assumptions I will need to use
locale th2 = th1 +
fixes
plus :: "'a ⇒ 'a ⇒ 'a"
assumes
arith_1: "plus n zero = n"
and plus_suc: "plus n (suc m) = suc ( plus n m)"
Any reference to the assumptions ends up having schematic variables and I can only replace them with free variables. How do I deal with the issue?
Any help is widely appreciate
Your locale definition does not mean what you think it does.
In your definition:
assumes
arith_1: "plus n zero = n"
and plus_suc: "plus n (suc m) = suc ( plus n m)"
means that n and m are fixed in the entire locale. What you really want is:
assumes
arith_1: "⋀n. plus n zero = n"
and plus_suc: "⋀n m. plus n (suc m) = suc ( plus n m)"
This seems a bit strange at first, because it is the same behavior as for the theorems, but not the most natural one.

Isabelle structure proof

There is a set of some structures. I'm trying to prove that the cardinality of the set equals some number. Full theory is too long to post here. So here is a simplified one just to show the idea.
Let the objects (which I need to count) are sets containing natural numbers from 1 to n. The idea of the proof is as follows. I define a function which transforms sets to lists of 0 and 1. Here is the function and its inverse:
fun set_to_bitmap :: "nat set ⇒ nat ⇒ nat ⇒ nat list" where
"set_to_bitmap xs x 0 = []"
| "set_to_bitmap xs x (Suc n) =
(if x ∈ xs then Suc 0 else 0) # set_to_bitmap xs (Suc x) n"
fun bitmap_to_set :: "nat list ⇒ nat ⇒ nat set" where
"bitmap_to_set [] n = {}"
| "bitmap_to_set (x#xs) n =
(if x = Suc 0 then {n} else {}) ∪ bitmap_to_set xs (Suc n)"
value "set_to_bitmap {1,3,7,8} 1 8"
value "bitmap_to_set (set_to_bitmap {1,3,7,8} 1 8) 1"
Then I plan to prove that 1) a number of 0/1 lists with length n equals 2^^n,
2) the functions are bijections,
3) so the cardinality of the original set is 2^^n too.
Here are some auxiliary definitions and lemmas, which seems useful:
definition "valid_set xs n ≡ (∀a. a ∈ xs ⟶ 0 < a ∧ a ≤ n)"
definition "valid_bitmap ps n ≡ length ps = n ∧ set ps ⊆ {0, Suc 0}"
lemma length_set_to_bitmap:
"valid_set xs n ⟹
x = Suc 0 ⟹
length (set_to_bitmap xs x n) = n"
apply (induct xs x n rule: set_to_bitmap.induct)
apply simp
sorry
lemma bitmap_members:
"valid_set xs n ⟹
x = Suc 0 ⟹
set_to_bitmap xs x n = ps ⟹
set ps ⊆ {0, Suc 0}"
apply (induct xs x n arbitrary: ps rule: set_to_bitmap.induct)
apply simp
sorry
lemma valid_set_to_valid_bitmap:
"valid_set xs n ⟹
x = Suc 0 ⟹
set_to_bitmap xs x n = ps ⟹
valid_bitmap ps n"
unfolding valid_bitmap_def
using bitmap_members length_set_to_bitmap by auto
lemma valid_bitmap_to_valid_set:
"valid_bitmap ps n ⟹
x = Suc 0 ⟹
bitmap_to_set ps x = xs ⟹
valid_set xs n"
sorry
lemma set_to_bitmap_inj:
"valid_set xs n ⟹
valid_set xy n ⟹
x = Suc 0 ⟹
set_to_bitmap xs x n = ps ⟹
set_to_bitmap ys x n = qs ⟹
ps = qs ⟹
xs = ys"
sorry
lemma set_to_bitmap_surj:
"valid_bitmap ps n ⟹
x = Suc 0 ⟹
∃xs. set_to_bitmap xs x n = ps"
sorry
lemma bitmap_to_set_to_bitmap_id:
"valid_set xs n ⟹
x = Suc 0 ⟹
bitmap_to_set (set_to_bitmap xs x n) x = xs"
sorry
lemma set_to_bitmap_to_set_id:
"valid_bitmap ps n ⟹
x = Suc 0 ⟹
set_to_bitmap (bitmap_to_set ps x) x n = ps"
sorry
Here is a final lemma:
lemma valid_set_size:
"card {xs. valid_set xs n} = 2 ^^ n"
Does this approach seem valid? Are there any examples of such a proof? Could you suggest an idea on how to prove the lemmas? I'm stuck because the induction with set_to_bitmap.induct seems to be not applicable here.
In principle, that kind of approach does work: if you have a function f from a set A to a set B and an inverse function to it, you can prove bij_betw f A B (read: f is a bijection from A to B), and that then implies card A = card B.
However, there are a few comments that I have:
You should use bool lists instead of nat lists if you can only have 0 or 1 in them anyway.
It is usually better to use existing library functions than to define new ones yourself. Your two functions could be defined using library functions like this:
set_to_bitmap :: nat ⇒ nat ⇒ nat set ⇒ bool list
set_to_bitmap x n A = map (λi. i ∈ A) [x..<x+n]
bitmap_to_set :: nat ⇒ bool list ⇒ nat set
bitmap_to_set n xs = (λi. i + n) ` {i. i < length xs ∧ xs ! i}```
Side note: I would use upper-case letters for sets, not something like xs (which is usually used for lists).
Perhaps this is because you simplified your problem, but in its present form, valid_set A n is simply the same as A ⊆ {1..n} and the {A. valid_set A n} is simply Pow {1..n}. The cardinality of that is easy to show with results from the library:
lemma "card (Pow {1..(n::nat)}) = 2 ^ n"
by (simp add: card_Pow)`
As for your original questions: Your first few lemmas are provable, but for the induction to go through, you have to get rid of some of the unneeded assumptions first. The x = Suc 0 is the worst one – there is no way you can use induction if you have that as an assumption, because as soon as you do one induction step, you increase x by 1 and so you won't be able to apply your induction hypothesis. The following versions of your first three lemmas go through easily:
lemma length_set_to_bitmap:
"length (set_to_bitmap xs x n) = n"
by (induct xs x n rule: set_to_bitmap.induct) auto
lemma bitmap_members:
"set (set_to_bitmap xs x n) ⊆ {0, Suc 0}"
by (induct xs x n rule: set_to_bitmap.induct) auto
lemma valid_set_to_valid_bitmap: "valid_bitmap (set_to_bitmap xs x n) n"
unfolding valid_bitmap_def
using bitmap_members length_set_to_bitmap by auto
I also recommend not adding "abbreviations" like ps = set_to_bitmap xs x n as an assumption. It doesn't break anything, but it tends to complicate things needlessly.
The next lemma is a bit trickier. Due to your recursive definitions, you have to generalise the lemma first (valid_bitmap requires the set to be in the range from 1 to n, but once you make one induction step it has to be from 2 to n). The following works:
lemma valid_bitmap_to_valid_set_aux:
"bitmap_to_set ps x ⊆ {x..<x + length ps}"
by (induction ps x rule: bitmap_to_set.induct)
(auto simp: valid_bitmap_def valid_set_def)
lemma valid_bitmap_to_valid_set:
"valid_bitmap ps n ⟹ valid_set (bitmap_to_set ps 1) n"
using valid_bitmap_to_valid_set_aux unfolding valid_bitmap_def valid_set_def
by force
Injectivity and surjectivity (which is your ultimate goal) should follow from the fact that the two are inverse functions. Proving that will probably be doable with induction, but will require a few generalisations and auxiliary lemmas. It should be easier if you stick to the non-recursive definition using library functions that I sketched above.

Using `defines` with induction

Consider following lemma which should be easily provable:
lemma
fixes n m::nat
defines "m ≡ n - 1"
shows "m ≤ n"
proof(induction n)
case 0
then show ?case unfolding m_def
(* Why does «n» appear here? *)
next
case (Suc n)
then show ?case sorry
qed
However after unfolding m, the goal becomes n - 1 ≤ 0 instead of 0 - 1 ≤ 0 rendering the goal unprovable since n = 2 is a counterexample.
Is this a bug in Isabelle? How can I unfold the definition correctly?
I think a useful explanation could be the following: Recall the definition of nat.induct, namely
?P 0 ⟹ (⋀n. ?P n ⟹ ?P (Suc n)) ⟹ ?P ?n
and note that ?n means that n is implicitly universally quantified, that is, the previous definition is equivalent to
⋀n. ?P 0 ⟹ (⋀n. ?P n ⟹ ?P (Suc n)) ⟹ ?P n
Now, when applying nat.induct to your example, clearly the first subgoal to prove is ?P 0, i.e., m ≤ 0. However, in that context, n is still an arbitrary but fixed nat, in particular it does not hold that n = 0, and that is the reason why after unfolding the definition of m you get n - 1 ≤ 0 as the new subgoal. With respect to your specific question, the problem is that you cannot prove your result by induction on n (but you can easily prove it using unfolding m_def by simp).
As Javier pointed out, the n defined in the lemma head is different from the n created by induction. In other words, any facts from "outside" that reference n are not directly usable within the proof (induction n) environment.
However, Isabelle does offer a way to "inject" such facts, by piping them into induction:
lemma
fixes n m::nat
defines "m ≡ n - 1"
shows "m ≤ n"
using m_def (* this allows induction to use this fact *)
proof(induction n)
case 0
then show ?case by simp
next
case (Suc n)
then show ?case by simp
qed
using assms will work just as well in this case.
Note that direcly referring to m_def is no longer necessary, since a version of it is included for each case (in 0.hyps and Suc.hyps; use print_cases inside the proof for more information).

Why can't we do case analysis on inductively defined predicates *directly* when doing rule inversion?

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.

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.

Resources