Local assumptions in "state" mode - isabelle

Frequently, when proving a statement in "prove" mode, I find myself in need of some intermediate statements that are not yet stated nor proved. To state them, I usually make use of the subgoal command, followed by proof- to change to "state" mode. In the process, however, all of the local assumptions are removed. A typical example could look like this
lemma "0 < n ⟷ ((2::nat)^n < 3^n)"
apply(auto)
subgoal
proof-
have "0<n" sorry (* here I would like to refer to the assumption from the subgoal *)
then show ?thesis sorry
qed
subgoal sorry
done
I am aware that I could state the assumptions using assume explicitly. However, this becomes quickly rather tedious when multiple assumptions are involved. Is there an easier way to simply refer to all of the assumptions? Alternatively, is there a good way to implement statements with short proofs directly in "prove" mode?

There is the syntax subgoal premises prems to bind the premises of the subgoal to the name prems (or any other name – but prems is a sensible default):
lemma "0 < n ⟷ ((2::nat)^n < 3^n)"
apply(auto)
subgoal premises prems
proof -
thm prems
There is also a method called goal_cases that automatically gives names to all the current subgoals – I find it very useful. If subgoal premises did not exist, you could do this instead:
lemma "0 < n ⟷ ((2::nat)^n < 3^n)"
apply(auto)
subgoal
proof goal_cases
case 1
By the way, looking at your example, it is considered a bad idea to do anything after auto that depends on the exact form of the proof state, such as metis calls or Isar proofs. auto is fairly brutal and might behave differently in the next Isabelle release so that such proofs break. I recommend doing a nice structured Isar proof here.
Also note that your theorem is a direct consequence of power_strict_mono and power_less_imp_less_base and can be proven in a single line:
lemma "0 < n ⟷ ((2::nat)^n < 3^n)"
by (auto intro: Nat.gr0I power_strict_mono)`

Related

Case analysis on a premise Isabelle

I have the following proof state:
1. ⋀i is s stk stack.
(⋀stack.
length (exec is s stack) = n' ⟹
length stack = n ⟹ ok n is n') ⟹
length (exec (i # is) s stack) = n' ⟹
length stack = n ⟹ ok n (i # is) n'
How do I perform a case split on i? Where i is of type:
datatype instr = LOADI val | LOAD vname | ADD
I'm doing this for exc 4.7 of concrete semantics so this should be possible to do with tactics.
If anything you should use cases i rule: instr.cases, but that will not work here because i is not a fixed variable but a bound variable. Also, the rule: instr.cases is not really needed because Isabelle will use that rule by default anyway.
Doing a case distinction on a bound variable without fixing it first is kind of discouraged; that said, it can be done by doing apply (case_tac i) instead of apply (cases i). But as I said, this is not the nice way to do it.
A more proper way to do it is to explicitly fix i using e.g. the subgoal command:
subgoal for i is s stk stack
apply (cases i)
An even better way would probably be to use a structured Isar proof instead.
However, I don't think the subgoal command or Isar proofs are something that you know about at this stage of the Concrete Semantics book, so my guess would be that there is a nicer way to do the proof where you don't have to do any manual case splitting.
Most probably you are doing an induction on the list of instructions; it would probably be better to do an induction on the predicate ok instead. But then again: Where is that predicate ok? I don't see it in your assumptions. It's hard to say what's going on there without knowing how you defined ok and what lemma you are trying to prove exactly and what tactics you applied already.

Proving theorem in Isar with no local assumptions

The statement n+0 = n is quite trivial to prove:
theorem add_0: "n+0 = (n::nat)"
apply(simp)
done
Upon trying to convert it to Isar however, I've noticed that it doesn't seem to require any assumption. So in the this attempt:
theorem add_0: "n+0 = (n::nat)"
proof -
thus "True" by simp
qed
It fails, as there are "No current facts available". This second attempt also fails:
theorem add_0: "n+0 = (n::nat)"
proof -
from add_0 show "True" by simp
qed
This time with the error "Failed to refine any pending goal".
Is it possible to prove a statement that requires no assume clause in Isar? If yes, then how?
There are two problems with thus "True"
As you've noted, the proof state that you begin doesn't have any assumptions, therefore no facts in the proof state. The abbreviation thus expands to then show, as we have no facts in our proof state it doesn't make sense to use then so we should instead replace it with show.
show "True" is saying that we want to prove "True" instead we want to prove the goal of the theorem. We can use the schematic variable ?thesis to refer to the original thesis of the theorem. The error Failed to refine any pending goal is just saying that if we were to prove that True is true, it wouldn't help solve the goal of our thesis.
So we can prove our original theorem with an Isar proof using the following pattern.
theorem add_0: "n+0 = (n::nat)"
proof -
show ?thesis by simp
qed

How to use lambda expression in Isabelle/HOL?

In my exercise to learn Isabelle/HOL syntax, I tried to prove a toy lemma below. It's about lambda expressions (and things like the "Greatest" notation that takes a predicate as input). The intended content of the lemma is that "the greatest natural number that is l.e. 1 is 1".
lemma "1 = Greatest (λ x::nat. x ≤ 1)"
proof -
show ?thesis
by auto
qed
However, the above proof doesn't work either by auto or simp, and generates a message that.
Failed to finish proof⌂:
goal (1 subgoal):
1. Suc 0 = (GREATEST x. x ≤ Suc 0)
Can someone help explain what went wrong with the statement or how to prove this correctly (if the statement is correct)?
There is nothing wrong with the lemma, it's just that none of the rules for Greatest are declared in such a way that auto knows about them. Which is probably good, because these kinds of rules tend to mess with automation a lot.
You can prove your statement using e.g. the rule Greatest_equality:
lemma "1 = Greatest (λ x::nat. x ≤ 1)"
proof -
have "(GREATEST (x::nat). x ≤ 1) = 1"
by (rule Greatest_equality) auto
thus ?thesis by simp
qed
You can find rules like this using the Query panel in Isabelle/jEdit or the find_theorems command by searching for the constant Greatest.
If the GREATEST thing confuses you, the syntax GREATEST x. P x is just fancy syntax for Greatest (λx. P x). Such notation is fairly standard in Isabelle, we also have ∃x. P x for Ex (λx. P x) etc.

How to test for falsity in implications?

In a complex lemma which is basically an implication one may mistakenly form an antecedent that turns out to be falsity. Is there any support in Isabelle for avoiding this situation?
You can use quickcheck for that. In a proof where you suspect your antecedents don't hold, locally try to prove False:
lemma "P ∧ ¬ P ⟹ foobar"
proof -
have False
quickcheck
In case it's an antecedent that you need frequently, you can also do it like this:
context
assumes "P ∧ ¬ P"
begin
lemma False
nitpick
quickcheck
end
The context command opens a new unnamed context with local hypotheses. When you exit the context, the assumption gets added to all theorems. There, you can also use nitpick to find problems.

Algebraic simplifications in Isabelle

I have been playing around with basic examples of proofs in Isabelle.
Consider the following simple proof:
lemma
fixes n::nat
shows "n*(n+1) = n^2 + n"
by simp
It seems to me that a powerful proof assistant like Isabelle should be able to prove this lemma without much guidance.
However, I was surprised to find out that Isabelle actually fails at applying the rule simp here (I also tried other "generic" rules like simp_all, auto, force, blast but the result is the same).
If I replace the last line by the following, then it works out:
by (simp add: power2_eq_square)
My concern is that I feel like I shouldn't have had to tell the system about the specific rule power2_eq_square to complete this proof.
Playing around with similar trivial examples, I found that simp is able to prove
n*(n+2)=n*n+n*2
but fails with
n*(n+3)=n*n+n*3
The last example is proven
by (simp add: distrib_left)
It is a complete mystery to me why I need to specify distrib_left in that second example, but not in the first (why is that?).
I have given these examples not for their own sake, but mainly to illustrate my main question:
Is there a way to automate the verification of routine algebraic identities such as the above in Isabelle? If there isn't, then why not? What are the technical obstacles?
Daily proof work indeed often stumbles over »routine algebraic identities«; but after some practical experience one usually develops some intuition how to solve such problems effectively. A pattern I have developed over the years, by example:
context semidom
begin
lemma "a * (b ^ 2 + c) + 2 = a * b * b + c * a + 2"
A typical explorative proof starts with
apply auto
Then associativity and commutative are considered also
apply (auto simp add: ac_simps)
Then more algebaic normalizing rules are applied
apply (auto simp add: algebra_simps)
The last gap is then easily filled by sledgehammer
apply (simp add: power2_eq_square)
After that, the proof can be compactified
by (simp add: algebra_simps power2_eq_square)
The lemma
lemma power2_eq_square: "a^2 = a * a"
is not a good rewrite rule in general, as it will easily blow up the size of terms. So it is expected that a term rewriting based automation like simp will not apply this without you telling it to.
What you want is some sort of proof search, and Isabelle provides that: After writing your lemma, you can invoke the sledgehammer tool, and it will readily and quickly find the proof for you:
Sledgehammering...
Proof found...
"z3": Try this: by (simp add: power2_eq_square) (1 ms)
"cvc4": Try this: by (simp add: power2_eq_square) (5 ms)

Resources