Inequality reasoning in Isabelle - isabelle

I have the following simple proof:
lemma
fixes a b n :: nat
assumes a: "a > n" "b > n"
shows "a*b > n*n"
proof -
from assms show "a*b > n*n" by(simp_all add: field_simps) ERROR
qed
In the proof state, Isabelle says:
Successful attempt to solve goal by exported rule:
n * n < a * b
But then:
Failed to apply initial proof method⌂: using this:
n < a
n < b goal (1 subgoal):
1. n * n < a * b
What is the problem?. Actually im interested in the actual single steps to do the proff, so i tought isabelle could show me the way.

field_simps is good for rearranging terms, but not so good for this kind of reasoning. When you want to prove something like this, you typically need a good rule for it; in this case something about (strict) inequalities and multiplication.
If you have something that looks trivial but you don't know how to prove it exactly and/or you don't know what the relevant facts are called in Isabelle, sledgehammer is often helpful:
from assms show "a*b > n*n"
sledgehammer
> Sledgehammering...
> Proof found...
> "cvc4": Try this:
> by (metis (no_types, lifting) dual_order.strict_trans gr_implies_not_zero
> linorder_neqE_nat mult.commute nat_0_less_mult_iff
> nat_mult_less_cancel1) (796 ms)
The problem with proofs found by sledgehammer is that, as you can see, they are frequently lengthy, slow, and not very illuminating. On the maintenance side of things, they are also somewhat fragile w.r.t. changes in the background theory. Still, it's a good place to start and you can often read relevant facts for your proof from the sledgehammer proofs (e.g. nat_mult_less_cancel1 here).
Another way to find relevant facts is the find_theorems command or, equivalently, the Query panel in the Isabelle/jEdit IDE. If you do
find_theorems "_ * _ > _ * _"
or, equivalently, enter _ * _ > _ * _ into the Query panel, you get a lot of output to read through, but some relevant facts hide at the end of that output, e.g. mult_strict_mono':
thm mult_strict_mono'
> ?a < ?b ⟹ ?c < ?d ⟹ 0 ≤ ?a ⟹ 0 ≤ ?c ⟹ ?a * ?c < ?b * ?d
Your proof then looks like this:
from assms show "a*b > n*n"
by (rule mult_strict_mono') simp_all
The simp_all just discharges the remaining proof obligations n ≥ 0.
Oh and by the way: The fact that you get a Successful attempt to solve goal but then an error message is a consequence of the non-linear nature of interactive Isabelle: When you write a by, the proof attempt is forked to the background and the processing of the proof document afterwards proceeds as if the proof had succeeded. This is in order to allow for parallelisation and to allow users to continue working on documents even if some proofs are broken.
The Successful attempt message comes from the part of Isabelle that is invoked after a show, and that part sees a successful (but still pending) proof of a*b > n*n. However, you then immediately get an error message from the by (simp_all …) saying that the proof method failed. In batch processing mode, failures such as this are more drastic (and more obvious).

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.

Quantifying over a specific part of the sentence in Isabelle

Suppose I write a lemma "(∀a. P a ⟹ Q a) ⟹ R b" in Isabelle. ∀a will only quantify over P a. If I want to quantify over P a ⟹ Q a however, putting parenthesis after ∀a (i.e "(∀a. (P a ⟹ Q a)) ⟹ R a") will cause Isabelle's parsing to fail.
How can I properly quantify over a specific part of the sentence?
Note: I know that free variables in lemmas are implicitly universal in Isabelle. This question is mostly for inner statements, in which the quantifier should not range over the whole sentence.
I believe that the parse failure stems from the fact that Isabelle/HOL has two distinct types of implication operators, Pure.imp (⟹) and HOL.implies (⟶). The former is part of the Isabelle metalogic while the latter is part of the HOL logic. You can find more information about why this distinction exists and when to use each in these mailing list posts:
https://lists.cam.ac.uk/pipermail/cl-isabelle-users/2018-December/msg00031.html
https://lists.cam.ac.uk/pipermail/cl-isabelle-users/2019-January/msg00019.html
The ⟹ operator has very low precedence, lower than ∀ and as a result ∀a. (P a ⟹ Q a) cannot be parsed as you would expect. You may fix the parse failure in your lemma by using the ⟶ operator instead which has higher precedence than ∀. Another option is to change ∀ to the meta quantifier ⋀, making your sentence more in line with Isabelle's framework.
A table of operator precedence is available but I cannot guarantee that it is up to date. You can use the print_syntax command in Isabelle for a more reliably up-to-date ordering.
Table: https://lists.cam.ac.uk/pipermail/cl-isabelle-users/2012-November/pdfi7tZP06fqA.pdf

Local assumptions in "state" mode

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)`

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)

How to replace ⋀ and ⟹ with ∀ and ⟶ in assumption

I'm an Isabelle newbie, and I'm a little (actually, a lot) confused about the relationship between ⋀ and ∀, and between ⟹ and ⟶.
I have the following goal (which is a highly simplified version of something that I've ended up with in a real proof):
⟦⋀x. P x ⟹ P z; P y⟧ ⟹ P z
which I want to prove by specialising x with y to get ⟦P y ⟹ P z; P y⟧ ⟹ P z, and then using modus ponens. This works for proving the very similar-looking:
⟦∀x. P x ⟶ P z; P y⟧ ⟹ P z
but I can't get it to work for the goal above.
Is there a way of converting the former goal into the latter? If not, is this because they are logically different statements, in which case can someone help me understand the difference?
That the two premises !!x. P x ==> P y and ALL x. P x --> P y are logically equivalent can be shown by the following proof
lemma
"(⋀x. P x ⟹ P y) ≡ (Trueprop (∀x. P x ⟶ P y))"
by (simp add: atomize_imp atomize_all)
When I tried the same kind of reasoning for your example proof I ran into a problem however. I intended to do the following proof
lemma
"⟦⋀x. P x ⟹ P z; P y⟧ ⟹ P z"
apply (subst (asm) atomize_imp)
apply (unfold atomize_all)
apply (drule spec [of _ y])
apply (erule rev_mp)
apply assumption
done
but at unfold atomize_all I get
Failed to apply proof method:
When trying to explicitly instantiate the lemma I get a more clear error message, i.e.,
apply (unfold atomize_all [of "λx. P x ⟶ P z"])
yields
Type unification failed: Variable 'a::{} not of sort type
This I find strange, since as far as I know every type variable should be of sort type. We can solve this issue by adding an explicit sort constraint:
lemma
"⟦⋀x::_::type. P x ⟹ P z; P y⟧ ⟹ P z"
Then the proof works as shown above.
Cutting a long story short. I usually work with Isar structured proofs instead of apply scripts. Then such issues are often avoided. For your statement I would actually do
lemma
"⟦⋀x. P x ⟹ P z; P y⟧ ⟹ P z"
proof -
assume *: "⋀x. P x ⟹ P z"
and **: "P y"
from * [OF **] show ?thesis .
qed
Or maybe more idiomatic
lemma
assumes *: "⋀x. P x ⟹ P z"
and **: "P y"
shows "P z"
using * [OF **] .
C.Sternagel answered your title question "How?", which satisfied your last sentence, but I go ahead and fill in some details based on his answer, to try to "help [you] understand the difference".
It can be confusing that there is ==> and -->, meta-implication and HOL-implication, and that they both have the properties of logical implication. (I don't say much about !! and !, meta-all and HOL-all, because what's said about ==> and --> can be mostly be transferred to them.)
(NOTE: I convert graphical characters to equivalent ASCII when I can, to make sure they display correctly in all browsers.)
First, I give some references:
[1] Isabelle/Isar Reference manual.
[2] HOL/HOL.thy
[3] Logic in Computer Science, by Huth and Ryan
[4] Wiki sequent entry.
[5] Wiki intuitionistic logic entry.
If you understand a few basics, there's nothing that confusing about the fact that there is both ==> and -->. Much of the confusion departs, and what's left is just the work of digging through the details about what particular source statements mean, such as the formula of C.Sternagel's first lemma.
"(!!x. P x ==> P y) == (Trueprop (!x. P x --> P y))"
C.Sternagel stopped taking the time to give me important answers, but the formula he gives you above is similar to one he gave me a while ago, to convince me that all free variables in a formula are universally quantified.
Short answer: The difference between ==> and --> is that ==> (somewhat) plays the part of the turnstile symbol, |-, of a non-generalized sequent in which there is only one conclusion on the right-hand side. That is, ==>, the meta-logic implication operator of Isabelle/Pure, is used to define the Isabelle/HOL implication object-logic operator -->, as shown by impI in the following axiomatization in HOL.thy [2].
(*line 56*)
typedecl bool
judgment
Trueprop :: "bool => prop"
(*line 166*)
axiomatization where
impI: "(P ==> Q) ==> P-->Q" and
mp: "[| P-->Q; P |] ==> Q" and
iff: "(P-->Q) --> (Q-->P) --> (P=Q)" and
True_or_False: "(P=True) | (P=False)"
Above, I show the definition of three other axioms: mp (modus ponuns), iff, and True_or_False (law of excluded middle). I do that to repeatedly show how ==> is used to define the axioms and operators of the HOL logic. I also threw in the judgement to show that some of the sequent vocabulary is used in the language Isar.
I also show the axiom True_or_False to show that the Isabelle/HOL logic has an axiom which Isabelle/Pure doesn't have, the law of excluded middle [5]. This is huge in answering your question "what is the difference?"
It was a recent answer by A.Lochbihler that finally gave meaning, for me, to "intuitionistic" [5]. I had repeatedly seen "intuitionistic" in the Isabelle literature, but it didn't sink in.
If you can understand the differences in the next source, then you can see that there's a big difference between ==> and -->, and between types prop and bool, where prop is the type of meta-logic propositions, as opposed to bool, which is the type of the HOL logic proposition. In the HOL object-logic, False implies any proposition Q::bool. However, False::bool doesn't imply any proposition Q::prop.
The type prop is a big part of the meta-logic team !!, ==>, and ==.
theorem "(!!P. P::bool) == Trueprop (False::bool)"
by(rule equal_intr_rule, auto)
theorem HOL_False_meta_implies_any_prop_Q:
"(!!P. P::bool) ==> PROP Q"
(*Currently, trying by(auto) will hang my machine due to blast, which is know
to be a problem, and supposedly is fixed in the current repository. With
`Auto methods` on in the options, it tries `auto`, thus it will hang it.*)
oops
theorem HOL_False_meta_implies_any_bool_Q:
"(!!P. P::bool) ==> Q::bool"
by(rule meta_allE)
theorem HOL_False_obj_implies_any_bool_Q:
"(!P. P::bool) --> Q::bool"
by(auto)
When you understand that Isabelle/Pure meta-logic ==> is used to define the HOL logic, and other differences, such as that the meta-logic is weaker because of no excluded middle, then you understand that there are significant differences between the meta-operators, !!, ==>, and ==, in comparison to the HOL object-logic operators, !, -->, and =.
From here, I put in more details, partly to convince any expert that I'm not totally abusing the word sequent, where my use here is based primarily on how it's used in reference [3, Huth and Ryan].
Attempting to not write a book
I throw in some quotes and references to show that there's a relationship between sequents and ==>.
From my research, I can't see that the word "sequent" is standardized. As far as I can tell, in [3.pg 5], Huth and Ryan use "sequent" to mean a sequent which has only has one conclusion on the right-hand side.
...This intention we denote by
phi1, phi2, ..., phiN |- psi
This expression is called a sequent; it is valid if a proof can be found.
A more narrow definition of sequent, in which the right-hand side has only one conclusion, matches up very nicely with the use of ==>.
We can blame L.Paulson for confusing us by separating the meta-logic from the object-logic, though we can thank him for giving us a larger logical playground.
Maybe to keep from clashing with the common definition of a sequent, as in [4, Wiki], he uses the phrase natural deduction sequent calculus in various places in the literature. In any case, the use of ==> is completely related to implementing natural deduction rules in the logic of Isabelle/HOL.
Even with generalized sequents, L.Paulson prefers the ==> notation:
Logic and Proof course 2012-13
Course materials: see slides for his generalized sequent calculus notation
You asked about differences. I throw in some source related to C.Sternagel's answer, along with the impI axiomatization again:
(*line 166*)
axiomatization where
impI: "(P ==> Q) ==> P-->Q"
(*706*)
lemma --"atomize_all [atomize]:"
"(!!x. P x) == Trueprop (ALL x. P x)"
by(rule atomize_all)
(*715*)
lemma --"atomize_imp [atomize]:"
"(A ==> B) == Trueprop (A --> B)"
by(rule atomize_imp)
(*line 304*)
lemma --"allI:"
assumes "!!x::'a. P(x)"
shows "ALL x. P(x)"
by(auto simp only: assms allI
I put impI in structured proof format:
lemma impI_again:
assumes "P ==> Q"
shows "P --> Q"
by(simp add: assms)
Now, consider ==> to be the use of the sequent turnstile, and shows to be the sequent notation horizontal bar, then you have the following sequent:
P |- Q
-------
P --> Q
This is the natural deduction implication introduction rule, as the axiom name says, impI (Cornell Lecture 15).
The Big Guys have been on top of all of this for a long time. See [1, Section 2.1, page 27] for an overview of !!, ==>, and ==. In particular, it says
The Pure logic [38, 39] is an intuitionistic fragment of higher-order logic
[13]. In type-theoretic parlance, there are three levels of lambda-calculus with
corresponding arrows =>/!!/==>`...
One general significance of the statement is that in the use of Isabelle/HOL, you are using two logics, a meta-logic and an object-logic, where those two terms come from L.Paulson, and where "intuitionistic" is a key defining point of the meta-logic.
See also [1, Section 9.4.1, Simulating sequents by natural deduction, pg 206]. According to M.Wenzel on the IsaUsersList, L.Paulson wrote this section. On page 205, Paulson first takes the definition of a sequent to be the generalized definition. On page 206, he then shows how you can line up one type of sequent with the use of ==>, which is by negating every proposition on the right-hand side of a sequent, except for one of them.
That, by all appearances, is a horn clause, which I know nothing about.
It seems obvious to me that using ==> is the use of a limited form of sequents. In any event, that's how I think of it, and thinking that way has given me an understanding of the differences between ==> and -->, along with the fact that the meta-logic has no excluded middle.
If A.Lochbhiler wouldn't have pointed out the absence of an excluded middle, I wouldn't have seen an important difference of what's possible with ==>, and what's possible with -->.
Maybe C.Sternagel will start back again to give me some of his important answers.
Please pardon the long answer.
Others have already explained some of the reasons behind the difference between meta-logic and logic, but missed the simple tactic apply atomize:
lemma "⟦⋀(x::'a). P x ⟹ P z ; P y⟧ ⟹ P z"
apply atomize
which yields the goal:
⟦ ∀x. P x ⟶ P z; P y ⟧ ⟹ P z
as desired.
(The additional type constraint ⋀(x::'a) is required for the reasons mentioned by chris.)
There is a lot of text already, so just a few brief notes:
Isabelle/Pure is minimal-higher order logic with the main connectives ⋀ and ⟹ to lay out Natural Deduction rules in a declarative way. The system knows how to compose them by basic means, e.g. in Isar proofs, proof methods like rule, attributes like OF.
Isabelle/HOL is full higher-order logic, with the full set of predicate logic connectives, e.g. ∀ ∃ ∧ ∨ ¬ ⟶ ⟷, and much more library material. Canonical introduction rules like allI, allE, exI, exE etc. for these connectives explain formally how the reasoning works wrt. the Pure framework. HOL ∀ and ⟶ somehow correspond to Pure ⋀ and ⟹, but they are of different category and should not be thrown into the same box.
Note that apart from the basic thm command to print such theorems, it occasionally helps to use print_statement to get an Isar reading of these Natural Deduction reasoning forms.

Resources