I try to learn Isabelle/HOL by working through this tutorial. I have a problem with the exercise about the double function. I defined it like this:
fun double :: "nat ⇒ nat" where
"double 0 = add 0 0" |
"double (Suc m) = Suc(Suc (double m))"
But when trying to prove that double m = add m m in my theorem:
theorem add_d [simp] : "double x = add x x"
apply(induction x)
apply(auto)
done
apply(auto) never gets evaluated (it's background is pink(?)). The same exercise asks to prove the commutativity and associativity of add and this worked ok. I am using Isabelle2014 with the default (Jedit) IDE.
In the code from your comment many theorems have [simp] attribute that tells Isabelle to add them to the simpset. However some of them are not suitable as a rewriting rule (used by simp and by auto), because they lead to a term to which the same rule can be applied again, causing an infinite loop. Removing the offending theorem from the simpset does the trick:
theorem add_d: "double x = add x x"
apply (induction x)
apply (auto simp del: add_zero)
done
But a better solution is to avoid adding such theorems to the simpset in the first place, e.g. to remove [simp] attribute from their declaration:
lemma add_zero: "add x 0 = add 0 x"
apply (induction x)
apply (auto)
done
After that add_d does not need the simp del part. But some other theorems may need to be explicitly told to use this particular rule, e.g. this one:
theorem add_com: "add x y = add y x"
apply (induction x)
apply (auto simp add: add_zero)
done
Note that, compared to your code, the [simp] attribute is dropped again to avoid circular rewriting if add_com is used elsewhere.
While Alexander's advice about simp-rules is sound, let me give some further comments.
Firstly, in your definition of double, I would replace add 0 0 by 0. As a general advice: prefer simple expressions over complex ones.
Secondly, Isabelle's simplifier is actually "smart" enough to handle rewriting with AC (i.e., associativity and commutativity) without looping, i.e., your two lemmas
lemma add_assoc [simp]:
"add (add x y) z = add x (add y z)"
and
lemma add_commute [simp]:
"add x y = add y x"
are fine as simp-rules (i.e., you can keep the [simp] attribute). The cause of the loop you are experiencing is your lemma
(*BAD*)
lemma add_zero [simp]:
"add x 0 = add 0 x"
Since having it in the simpset (Isabelle jargon for the set of all simplification rules that are used by methods like simp and auto) allows for the infinite derivation:
add 0 0 = add 0 0 = add 0 0 = ...
I would suggest to replace it by
lemma add_zero [simp]:
"add x 0 = x"
Then all your proofs (provided they are stated in the correct order) should be "automatic" in the sense
apply (induction ...)
apply auto
done
which can be abbreviated to
by (induction ...) (auto)
Related
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.
I'm using Isabelle/HOL, trying to prove a statement Q. On the way to proving Q, I have proven the existence of a natural number that satisfies P::"nat=>bool". How can I create an instance x::nat that satisfies P, so that I can reference it in subsequent lemmas?
Inside any given lemma, I can do it using the obtains command. I want to reference the same witness instance in a number of different lemmas, however, so I need a way to do it outside of any lemma. I tried to use fix/assume inside a new locale, as shown below:
locale outerlocale
fixes a b c ...
begin
definition Q::bool where ...
lemma existence: "EX x. P x"
proof -
...
qed
locale innerlocale = outerlocale +
fixes x::nat
assumes "P x"
begin
(*lots of lemmas that reference x*)
lemma innerlemma0
...
lemma innerlemma7
proof -
...
qed
lemma finalinnerlemma: "Q"
proof -
...
...
qed
end (*innerlocale*)
lemma outerlemma: "Q"
proof -
(*I don't know what goes here*)
qed
end (*outerlocale)
Unfortunately this just kicks the can down the road. I need a way to use the existence lemma to extract the final inner lemma into the outer locale. If I try to interpret the inner locale, I'm once again up against the problem of supplying a witness. I can't interpret locales inside lemmas (unless I'm misunderstanding the error I get), and I can't use obtain outside of lemmas, so I'm stuck.
So it looks I need to figure out either
how to specify a witness instance outside a lemma or
how to extract a lemma from a locale by proving that locale's assumptions
Or is there a better way to do what I'm trying to do? Thanks!
You can just use SOME x. P x, e.g., in a definition:
definition my_witness :: nat where
"my_witness = (SOME x. P x)"
and then use thm someI_ex to show P my_witness.
I define a very simple function replace which replaces 1 with 0 while preserving other input values. I want to prove that the output of the function cannot be 1. How to achieve this?
Here's the code.
theory Question
imports Main
begin
fun replace :: "nat ⇒ nat" where
"replace (Suc 0) = 0" |
"replace x = x"
theorem no1: "replace x ≠ (Suc 0)"
sorry
end
Thanks!
There exist several approaches for proving the statement that you are trying to prove.
You can make an attempt to use sledgehammer to find the proof automatically, e.g.
theorem no1: "replace x ≠ (Suc 0)"
by sledgehammer
(*using replace.elims by blast*)
Once the proof is found, you can delete the explicit invocation of the command sledgehammer.
Perhaps, a slightly better way to state the proof found by the sledgehammer would be
theorem no1': "replace x ≠ (Suc 0)"
by (auto elim: replace.elims)
You can also try to provide a more specialized proof. For example,
theorem no1: "replace x ≠ (Suc 0)"
by (cases x rule: replace.cases) simp_all
This proof looks at the different cases the value of x can have and then uses simplifier (in conjunction with the simp rules provided by the command fun during the definition of your function) to finish the proof. You can see all theorems that are generated by the command fun by typing print_theorems immediately after the specification of replace, e.g.
fun replace :: "nat ⇒ nat" where
"replace (Suc 0) = 0" |
"replace x = x"
print_theorems
Of course, there are other ways to prove the result that you are trying to prove. One good way to improve your ability to find such proofs is by reading the documentation and tutorials on Isabelle. My own starting point for learning Isabelle was the book "Concrete Semantics" by Tobias Nipkow and Gerwin Klein.
Suppose some lemma, lets call it an_equation, proves that equation f(n)=n*n+1 holds for all odd natural numbers n (and f is some previously defined function).
How can I instantiate this lemma to concrete values of n, so that I can prove, say f(5)=5*5+1?
(There is the lemmas keyword, with which I can prove lemmas inst = an_equation[where n=5, simplified], but this is not quite what I what. What I want is
lemma inst_new : "f(5) = 5*5+1"
but as there were scarcely any example around in the usual documentation thai I consult, I couldn't figure out how to prove this.)
You can instantiate free variables in your theorem with the of and where attributes, e.g. an_equation[of 5] or an_equation[where n = 5]. You can also instantiate its assumptions, e.g. if you have a theorem called foo of the form P x ⟹ Q x and you have a theorem called bar of the form P 5, you can do foo[OF bar] to get the theorem Q 5.
You can inspect these instantiated with thm (e.g. thm foo[of 5]) and use them in a proof with using, from, etc.
Note that proof methods also instantiate theorems whenever needed, e.g. if you have, as above, the theorem foo stating that P n ⟹ Q n and you have the goal Q 5, you can do this:
lemma "Q 5"
apply (rule foo)
Then the goal state will have one subgoal, namely P 5.
So, in your case, by (rule an_equation) should do the trick. Something like by (simp add: an_equation) or using an_equation by simp or using an_equation by blast would probably also work, but might be slightly less robust in general since the simplifier can do other stuff first and then the rule might not apply anymore.
I am trying to understand the lemma below.
Why is the ?y2 schematic variable introduced in exI?
And why it is not considered in refl (so: x = x)?
lemma "∀x. ∃y. x = y"
apply(rule allI) (* ⋀x. ∃y. x = y *)
thm exI (* ?P ?x ⟹ ∃x. ?P x *)
apply(rule exI) (* ⋀x. x = ?y2 x *)
thm refl (* ?t = ?t *)
apply(rule refl)
done
UPDATE (because I can't format code in comments):
This is the same lemma with a different proof, using simp.
lemma "∀x. ∃y. x = y"
using [[simp_trace, simp_trace_depth_limit = 20]]
apply (rule allI) (*So that we start from the same problem state. *)
apply (simp only:exI)
done
The trace shows:
[0]Adding rewrite rule "HOL.exI":
?P1 ?x1 ⟹ ∃x. ?P1 x ≡ True
[1]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
⋀x. ∃y. x = y
[1]Applying instance of rewrite rule "HOL.exI":
?P1 ?x1 ⟹ ∃x. ?P1 x ≡ True
[1]Trying to rewrite:
x = ?x1 ⟹ ∃xa. x = xa ≡ True <-- NOTE: not ?y2 xa or similar!
[2]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
x = ?x1
[1]SUCCEEDED
∃xa. x = xa ≡ True
So apparently simp and rule handles exI differently. And the remaining question is: what is the mechanical (programmatical) reasoning behind rule's behavior.
When you use rule thm for some fact thm, Isabelle performs higher-order unification of the conclusion of thm with the current goal. If there is a unifier, it is used to instantiate both the goal and the conclusion of the theorem, and then resolution is performed (i.e. the goal is replaced with the assumptions of thm).
This means that:
Schematic variables in the goal can be instantiated by rule through unification
Variables that appear only in the assumptions of thm will not be instantiated by the unification and will therefore remain schematic. That way, you end up with schematic variables in your new goals. Such variables can be seen as existential in some sense, because the conclusion of thm holds if you can prove the assumptions for just one arbitrary value.
In the case of exI, you have ?P ?x ⟹ ∃x. ?P x. When you apply rule exI, the variable ?P is instantiated to λy. x = y, but the variable ?x appears only in the assumptions of exI, so it remains schematic. This means that you can pick any value you want for ?x later on in your proof.
To be more precise, you end up with ⋀x. x = ?y2 x as your goal. You might ask ‘Why not just ⋀x. x = ?y2?’ That would mean that you have to show that x equals some fixed value y2 for all possible values of x. That is obviously not true in general. ⋀x. x = ?y2 x means you have to show that every x equals some y2 that may depend on x – or, equivalently, that there is a function y2 that, when given x, outputs x.
Of course, there is such a function and it is simply the identity function λx. x. That is precisely what ?y2 gets instantiated to when you apply rule refl: the goal x = ?y2 x is unified with the conclusion of refl ?t = ?t and you end up with ?t = x and ?y2 = λx. x, and since refl has no assumptions, this resolution finishes the proof.
I am not entirely sure what you mean with ‘And why it is not considered in refl?’, but I hope that I have answered your questions.
Get a more complete answer from an expert, but I give a short, brief answer to your second part.
The great thing about Isabelle is that it provides many different ways to prove a problem.
Your new question is similar to L.Paulson's comment on FOM: you moved the goal post by switching the question to rule vs. simp:
http://www.cs.nyu.edu/pipermail/fom/2015-October/019312.html
Getting a basic understanding of simp is actually a much easier goal to pursue, or I wouldn't be adding my reponse here.
rule and natural deduction
The use of rule is the use of natural deduction (ND), where most people aren't up to speed on ND. The use of ND requires understanding ND, so questions like your first question can lead to a non-simple answer, because anything informative can't be a one-liner answer, especially due to things like schematic variables (which you asked about), resolution, unification, rewriting, etc.
Do a search on natural deduction and you'll find the standard wiki page about it. There are numerous books on natural deduction, though they get swamped in searches on "logic" due to first-order logic books. A popular book is Logic in Computer Science, 2nd, by Huth and Ryan.
If you study ND, you'll see that exI matches one of the ND rules.
I have yet to take the time to come up to speed on ND, because I keep making progress without having more than a basic understanding of ND.
Sledgehammer, and auto-methods auto, simp, blast, induct, cases, etc., and Sledgehammer's use of some of those, keep me from finding the time to become good with natural decution.
Answer's like M.Eberl's, though not simple explanations, help me absorb a little here and a little there.
Simp, I think of it as simple substitution (rewriting)
The mechanics behind simp is really simple, compared to natural deduction. You define a formula and prove it:
lemma foo [simp]: "left_hand_side = right_hand_side"
In the proof of another theorem, when simp is invoked in one way or another, or foo is unfolded, where there is left_hand_side, it's replaced with right_hand_side. It's just classic mathematical substitution.
I suppose it could also be "rewriting", but I don't know anything about rewriting, other than they talk about it.
There are lots of details about how and whether one should set things up automatically (to prevent looping), like with [simp] or declare foo_def [simp add], but that's just details along the line of normal programming.