Reordering goals (Isabelle) - isabelle

I would like to know how to reorder goals in the following situation:
lemma "P=Q"
proof (rule iffI, (*here I would like to swap goal order*), rule ccontr)
oops
I would like a solution that doesn't involve changing the lemma statement. I realise that prefer and defer can be used in apply-style proofs, but I would like to have a method that can be used in the proof (...) part.
Edit:
As Andreas Lochbihler says, writing rule iffI[rotated] works in the above example. However, is it possible to swap the goal order in the following situation without changing the statement of the lemma?
lemma "P==>Q" "Q==>P"
proof ((*here I would like to swap goal order*), rule ccontr)
oops
This example may seem contrived, but I feel that there may be situations where it is inconvenient to change the statement of the lemma, or it is necessary to swap goal order when there is no previous application of a rule such as iffI.

The order of the subgoals is determined by the order of the assumptions of the rule you apply. So it suffices to swap the assumptions of the iffI rule, e.g., using the attribute [rotated] as in
proof(rule iffI[rotated], rule ccontr)
In general, there is no proof method to change the order of the goals. And if you are thinking about using this with more sophisticated proof automation like auto, I'd heavily advise you against doing these kinds of things. Proof scripts with lots of automation in it should work independently of the order of the goals. Otherwise, your proofs will break easily when something in the proof automation setup changes.
However, a few low-level proof tactics allow to use explicit goal addressing (mostly those that end on _tac). For example,
proof(rule iffI, rule_tac [2] ccontr)
applies the ccontr rule to the second subgoal instead of the first.

Related

Object level implication in Isabelle/HOL

I see that many theorems in Isabelle/HOL prefer meta-level implication:
==>
instead of
-->
the object logic level, i.e. higher order logic implication.
Isabelle wiki says that roughly speaking, meta level implication should be used to separate the assumptions from the conclusion in rule statements.
Other than that, what should I know about the use of object and meta level implication? I see the latter is being used mostly. When and for what should I use HOL implication?
I think the short answer is: Use ==> whenever possible as it is easier to work with than -->.
That being said, you should not see ==> too often in the code you write.
When writing a Lemma, it is often nicer to use the assumes and shows syntax.
For intermediate steps with have there is a syntax with if:
have "B" if "A" instead of have "B ==> A"
The meta implication can only be used at the top level, so if you have a predicate as argument to a function you cannot use ==> in that predicate.

Error message in Isabelle/HOL

When applying the wrong tactic or the wrong deduction rule, the error message is usually too general:
Failed to apply initial proof method⌂
I am using Isabelle to teach natural deduction. When Isabelle complains, some students change the rule/tactic arbitrary without reflecting on the possible causes of the error. A more detailed error message could be part of the learning process of Isabelle, I think.
How to make those error messages student friendly? Does that require editing the source code or can it be managed by defining more expressive tactics of natural deduction?
Tactics in Isabelle can be thought of as chainable non-deterministic transformations of the goal state. That means that the question of what specifically caused a tactic to fail is difficult to answer in general, and there is no mechanism to track such information in Isabelle's tactic system. However, one could relatively easily modify existing tactics such that they can optionally output some tracing information.
However, I have no idea what this information should be. There are simple tactics such as rule where the reason why applying it fails is always that the rule that it is given cannot be unified with the goal (and possibly chained facts), and there are similarly simple tactics like intro, drule, frule, erule, and elim. Such unification-related problems can be debugged quite well sometimes using declare [[unify_trace_failure]], which prints some tracing information every time a unification fails.
With simp and auto, the situation is much less clear because of how many different things these methods can do. Essentially, when the proof method could not be applied at all, it means that ‘none of the things that simp and auto can do worked for this goal’. For simp, this includes simplification, splitting, linear arithmetic, and probably a lot more things that I forgot. For auto, it additionally includes classical reasoning with a certain search depth. One cannot really say easily what specific thing went wrong when these methods fail.
Some specialised tactics do print more specific error messages if something goes wrong, e.g. sat and smt sometimes print a special error message when they have found a counterexample to the goal, but I cannot even imagine what more helpful output for something like simp or auto would look like. If you have an idea, please do tell me.
I think this problem cannot really be solved with error messages; one must simply get to know the system and the tactics one uses better and understand what they do and when they fail. Perhaps it would be good to have a kind of catalogue of commonly-used tactics that mentions these things.

Complete proof output in Isabelle

If Isabelle did not find a proof for a lemma, is it possible to output everything that was done by all the proof methods that were employed in order to arrive at the subgoals, at which they couldn't proceed any further ? This would help me see, at which avenues they got stuck, which then would help me to point them in the right direction.
(And also for completed proofs I would find it interesting to have a complete proof log that shows all the elementary inferences that were performed to proof some lemma.)
This question sounds similar to this one, which I answered a few days ago. Parts of that answer also apply here. Anyway, to answer this question specifically:
Not really. For most basic proof methods (rule et al., intro, fact, cases, induct) it is relatively straightforward what they do and when they fail, it is pretty much always because the rule they tried to apply does not unify with the goals/premises that they are given. (or they don't know which rule to apply in the first place)
You were probably thinking more of more automatic tactics like blast, force, simp, and auto. Most of them (blast, force, fastforce, fast, metis, meson, best, etc.) are ‘all-or-nothing’: They either solve the subgoal or they do nothing at all. It is therefore a bit tricky to find out where they get stuck and usually people use auto for this kind of exploration: You apply auto, look at the remaining subgoals, and think about what facts/parameters you could add in order to break down those more.
The situation with simp is similar, except that it does less than auto. simp is the simplifier, which uses term rewriting, custom rewriting procedures called simprocs, certain solvers (e.g. for linear arithmetic), and a few other convenient things like splitters to get rid of if expressions. auto is basically simp combined with classical reasoning, which makes it a lot more powerful than simp, but also less predictable. (and occasionally, auto does too much and thereby turns a provable goal into an unprovable goal)
There are some tracing tools (e.g. the simplifier trace, which is explained here). I thought there also was a way to trace classical reasoning, but I cannot seem to find it anymore; perhaps I was mistaken. In any case, tracing tools can sometimes help to explain unexpected behaviour, but I don't think they are the kind of thing you want to use here. The better approach is to understand what kinds of things these methods try, and then when simp or auto returns a subgoal, you can look at those and determine what you would have expected simp and auto to do next and why it didn't do that (usually because of some missing fact) and fix it.

How to submit an argument to keyword proof?

I would like to understand how keyword proof works in an Isar proof. I consulted the Isabelle/Isar reference, section 6.3.2 and Programming and Proving in Isabelle/HOL, section 4.1.
To summarise what I have learned, there are three ways of beginning a proof by the keyword proof:
without any argument Isabelle finds a suitable introduction rule to the lemma being proved and applies it in a forward mode
if a hyphen: - is supplied as an argument, then proof does nothing to the goal, avoiding any automatic rule being applied when it would lead to a blind alley
if a specific rule, like rule name, unfold true_def, clarify or induct n is supplied, then it is applied to the goal in a forward mode
Am I right that the third case is like using apply with the argument supplied?
How is the automatic introduction rule in the first case picked by the system?
And, does the above fully describe the usage of proof?
The command proof without a method specification applies the method default. The method default is almost like rule, but if rule fails, then it tries next intro_classes and then unfold_locales. The method rule without being given a list of theorems tries all rules that are declared to the classical reasoner (intro, elim, and dest). If no facts are chained in, only intro rules are considered. Otherwise, all types of rules are tried. All chained-in facts must unify with the rules. dest rules are transformed into elim rules before they are applied.
You can print all declared rules with print_rules. Safe rules (intro!, elim!, ...) are preferred over normal rules (intro, elim, ...) and extra rules (intro?, elim?) come last.
You can also use rule without giving any rules. Then, it behaves like default, but without the fallbacks intro_classes and unfold_locales.
Andreas gave a good description of how proof without a method argument works; I'll just cover some other parts of the question.
First, proof (method) is like apply (method) except for one thing: While apply leaves you in "prove" mode, where you can continue with more apply statements, proof transitions into "state" mode, where you must use a have or show statement to continue proving. Otherwise the effect on the goal state is the same.
I'd also like to point out that case 2 (proof -) is really an instance of case 3, because - is actually an ordinary proof method just like rule name or induct (you can also write apply -, for example). The hyphen - proof method does nothing, except that it will insert chained facts into the current goal, if it is given any chained facts.

Isabelle: Switching between "structured" and "apply-style" proofs

There are two styles of proof in Isabelle: the old "apply" style, where a proof is just a chain of
apply (this method)
apply (that method)
statements, and the new "structured" Isar style. Myself, I find both useful; "apply" style is more concise, and so is handy for uninteresting technical lemmas, while "structured" style is handy for the major theorems.
Sometimes I like to switch from one style to the other, mid-proof. Switching from "apply" style to "structured" style is easy: I just insert
proof -
in my apply-chain. My question is: how can I switch from "structured" style back to "apply" style?
To give a more concrete example: suppose I have five subgoals. I issue some "apply" instructions to despatch the first two subgoals. Then I launch into a structured proof to dispense with the third. I have two subgoals remaining: how do I return to "apply" style for these?
You can continue in "apply" style within a structured proof by using apply_end instead of apply, but this is rarely seen in practice and only during explorative work. In a polished proof, you would just pick the subgoals that merit an Isar proof and finish off all the remaining subgoals in one method call after the qed, as there is no need to deal with the subgoals in any specific order.
Alternatively, you can use defer before you start the structured proof with proof and continue immediately with the other subgoals in "apply" style, i.e., you defer the goals with structured proofs until the end.
And finally, you can of course re-state your goal in the structured proof with fix/assume/show and continue with "apply" style there. But you have to do this for each remaining subgoal separately, so this might be a bit tedious. The default case names goal1, goal2, etc. help a bit with typing, but such proofs are typically hard to maintain (especially as apply_end changes the goal numbering for goal<n>).

Resources