Isabelle - Nitpick - using witness values automatically - isabelle

How can I automatically use the values found by nitpick, instead of using rule exI's and manually typing in the witness values?
theorem "EX a b. a + b = 5 & a - b = (1 :: int)"
nitpick [falsify=false]
(* Nitpicking formula...
Nitpick found a model:
Skolem constants:
a = 3
b = 2
*)
apply (rule exI[where x="3"])
apply (rule exI[where x="2"])
apply (simp)
done

I don't think that functionality exists, since I'd say this is not a typical use case.
One could probably add something like this to the nitpick command with a relatively small amount of effort or create a new command to do it.

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.

Is there a way to name and refer to a assumption in Isabelle apply-scripts?

Is there any way in Isabelle (2021) to refer to assumptions in the old apply style proofs?
In particular, I am interested in using the assumptions as facts in the OF operator so that I can do (hypothetically):
apply(rule R[OF assm1 assm4])
, where assm1 and assm4 should refer to the 1st and 4th assumptions in the current proof state.
Often times, I can arrange assumptions of the current sugboal so that R[OF assm1 assm4] is the same as the subgoal. But then, I can't finish the proof because I don't know how to refer to assm1 assm4 etc. It seems that only global theorem names are allowed with OF.
I even tried to use the subgoal_tac method on the assumptions, but it does not seem to have an option of giving names to the fact.
In the end, I have to use an automatic script such as simp, which is somewhat opaque for something so obvious. By the way, this is for learning purposes. I tried setting up simp_trace, but still couldn't replicate the effect without using simp.
Moreover,
If there is no way to refer to assumptions, is this a limitation of the tactics or a fundamental limitation of natural deduction? (i.e. Is the rewriting style of R[OF assm1 assm4] not compatible with natural deduction?)
The whole point is Isar is that you can name assumptions...
The first low-level solution is to use drule (or frule to keep the assumptions).
Here is an example:
lemma
assumes ‹⋀x y. P x ⟹ Q y ⟹ R z› ‹P x› ‹Q y›
shows ‹R z›
using assms(2-) apply -
apply (drule assms(1))
apply assumption
apply assumption
done
Look at Chapter 5 for details on the destruction/elimination/intro rules.
The second solution is subgoal:
lemma
assumes ‹⋀x y. P x ⟹ Q y ⟹ R z› ‹P x› ‹Q y›
shows ‹R z›
using assms(2-) apply -
subgoal premises p
by (rule assms(1)[OF p])
done
but this creates hard-to-read proofs if you have very deep nesting.
The third and best solution is to use Isar proofs…
Here is a version that completely avoids using names:
lemma
assumes ‹⋀x y. P x ⟹ Q y ⟹ R z› ‹P x› ‹Q y›
shows ‹R z›
using assms apply -
apply (elim meta_allE[of _ x])
apply (elim meta_allE[of _ y])
apply (drule cut_rl)
apply assumption
apply (drule cut_rl)
apply assumption
apply assumption
done
You can see how ugly this is and why you should avoid that.

How to prove the existence of inverse functions in Isabelle/HOL?

I am trying to prove the following basic theorem about the existence of the inverse function of a bijective function (to learn theorem-proving with Isabelle/HOL):
For any set S and its identity map 1_S, α:S→T is bijective iff there
exists a map β: T→S such that βα=1_S and αβ=1_S.
Below is what I have so far after some attempts to define relevant things including functions and their inverses. But I am pretty stuck and couldn't make much progress due to my lack of understanding of Isabelle and/or Isar.
theory Test
imports Main
"HOL.Relation"
begin
lemma bij_iff_ex_identity : "bij_betw f A B ⟷ (∃ g. g∘f = restrict id B ∧ f∘g = restrict id A)"
unfolding bij_betw_def inj_on_def restrict_def iffI
proof
let ?g = "restrict (λ y. (if f x = y then x else undefined)) B"
assume "(∀x∈A. ∀y∈A. f x = f y ⟶ x = y)"
have "?g∘f = restrict id B"
proof
(* cannot prove this *)
end
In above, I try to give an explicit existential witness (i.e. the inverse function g of the original function f). I have several issues about the proof.
whether the concepts are defined right (functions, inverse functions etc.) in Isabelle terms.
how to expand the relevant definitions and then simplify them with function applications. I have followed some Isabelle (2021) examples/tutorials about both the apply-style simp, and structured style Isar proof but couldn't use the Isar proof fluently. Once I started the proof command, I don't know how to simp or move any further.
Isar has the new way of assumes ... shows ... for stating the theorem. Is there similar support for proving iff's (⟷) like the example above? Without it, there is no access to assms etc., and is it necessary to assume everything except the conclusion during the proof.
Can someone help explain how the above existential proof about inverse function can be accomplished?
lemma bij_iff_ex_identity : "bij_betw f A B ⟷ (∃ g. g∘f = restrict id B ∧ f∘g = restrict id A)"
I think this is not exactly what you want an I am doubtful that it is true. g∘f = restrict id B does not mean that g∘f and id are equal on B. It means that the total function g∘f (and there are only total functions in HOL) equals the total function restrict id B. The latter returns id x on x∈B and undefined otherwise. So to make this equality true, g needs to output undefined whenever the input of f is not in B. But how would g know that!
If you want to use restrict, you could write restrict (g∘f) B = restrict id B. But personally, I would rather go for the simpler (∀x∈B. (g∘f) x = x).
So the corrected theorem would be:
lemma bij_iff_ex_identity : "bij_betw f A B ⟷ (∃ g. (∀x∈A. (g∘f) x = x) ∧ (∀y∈B. (f∘g) y = y))"
(Which is still wrong, by the way, as quickcheck tells me in Isabelle/jEdit, see the output window. If A has one element and B is empty, f cannot be a bijection. So the theorem you are attempting is actually mathematically not true. I will not attempt to fix it, but just answer the remaining lines.
unfolding bij_betw_def inj_on_def restrict_def iffI
The iffI here has no effect. Unfolding can only apply theorems of the form A = B (unconditional rewriting rules). iffI is not of that form. (Use thm iffI to see.)
proof
Personally, I don't use the bare form proof but always proof - or proof (some method). Because proof just applies some default method (in this case, equivalent to (rule iffI), so I think it's better to make it explicit. proof - just starts the proof without applying an extra method.
let ?g = "restrict (λ y. (if f x = y then x else undefined)) B"
You have an unbound variable x here. (Note the background color in the IDE.) That is most likely not what you want. Formally, it is allowed, but x will be treated as if it was some arbitrary constant.
Generally, I don't think there is any way to define g in a simple way (i.e., only with quantifiers and function applications and if-then-else). I think the only way to define an inverse (even if you know it exists), is to use the THE operator, because you need to say something like g y is "the" x such that f x = y. (And then later in the proof you will run into a proof obligation that it indeed exists and that it is unique.) See the definition of inv_into in Hilbert_Choice.thy (except it uses SOME not THE). Maybe for starters, try to do the proof just using the existing inv_into constant.
assume "(∀x∈A. ∀y∈A. f x = f y ⟶ x = y)"
All assume commands must have assumptions exactly as the are in the proof goal. You can test whether you wrote it right by just temporarily writing the command show A for A (that's an unprovable goal that would, however, finish the proof, so it tricks Isabelle into checking if it would). If this command does not give an error, you got the assumes right. In your cases, you didn't, it should be (∀x∈A. ∀y∈A. f x = f y ⟶ x = y) ∧ f ' A = B. (' is the backtick symbol here. Markup doesn't let me write it.)
My recommendation: Try the proof with bij instead of bij_betw first. (One direction is in BNF_Fixpoint_Base.o_bij if you want to cheat.)
Once done, you can try to generalize.
I agree with the insightful remarks provided by Dominique Unruh. However, I would like to mention that a theorem that captures the idea underlying the theorem that you are trying to prove already exists in the source code of the main library of Isabelle/HOL. In fact, it exists in at least two different formats: let me name them the traditional Isabelle/HOL format and the canonical FuncSet format. For the former one, see the theorem bij_betw_iff_bijections:
"bij_betw f A B ⟷ (∃g. (∀x ∈ A. f x ∈ B ∧ g(f x) = x) ∧ (∀y ∈ B. g y ∈ A ∧ f(g y) = y))"
The situation is a little bit more complicated with FuncSet. There does not seem to exist a single theorem that captures the idea. However, together, the theorems bij_betwI, bij_betw_imp_funcset and inv_into_funcset are nearly equivalent to the theorem that you are trying to state. Let me provide a sketch of how one could express this theorem in a manner that would be considered reasonably canonical in the FuncSet sense (try to prove it yourself):
lemma bij_betw_iff:
shows "bij_betw f A B ⟷
(
∃g.
(∀x. x∈A ⟶ g (f x) = x) ∧
(∀y. y∈B ⟶ f (g y) = y) ∧
f ∈ A → B ∧
g ∈ B → A
)"
sorry
I would also like to repeat the advice given by Dominique Unruh and provide several side remarks:
My recommendation: Try the proof with bij instead of bij_betw first.
Indeed, this is a very good idea. In general, by trying to restrict the problem to explicitly defined sets A and B, instead of working directly with types, you touched upon a topic that is known as relativization in logic. For a mild layman's introduction see, for example, https://leanprover.github.io/logic_and_proof/first_order_logic.html [1], for a slightly more thorough introduction in the context of set theory see [2, chapter 12]. As you have probably noticed by now, it is not that easy to relativize theorems in Isabelle/HOL and requires additional proof effort.
However, there exists an extension of Isabelle/HOL that allows for the automation of the process of the relativization of theorems. For more information about this extension see the article From Types to Sets by Local Type Definition in Higher-Order Logic by Ondřej Kunčar and Andrei Popescu [3]. There also exists a large scale application example of the framework [4]. Independently, I am working on making this extension more user-friendly and very slowly approaching the final stages in my efforts: see https://gitlab.com/user9716869/tts_extension. Thus, in principle, if you know how to use Types-To-Sets and you accept its axioms, then it is sufficient to prove the theorem with bij, e.g.,
"bij f ⟷ (∃g. (∀x. g (f x) = x) ∧ (∀y. f (g y) = y))",
Then, the theorems like
bij_betw_iff_bijections and bij_betw_iff can be synthesized automatically for free upon a click of a button (almost...).
Finally, for completeness, let me offer my own advice with regard to your queries (although, as I mentioned, I agree with everything stated by Dominique Unruh)
how to expand the relevant definitions and then simplify them with
function applications. I have followed some Isabelle (2021)
examples/tutorials about both the apply-style simp, and structured
style Isar proof but couldn't use the Isar proof fluently. Once I
started the proof command, I don't know how to simp or move any
further.
I believe that the best way to learn what you are trying to learn is by following through the exercises in the book Concrete Semantics by Tobias Nipkow and Gerwin Klein [5]. Additionally, I would also look through A Proof Assistant for Higher-Order Logic by Tobias Nipkow et al [6](it is slightly outdated, but I found it to be useful specifically for learning apply-style scripting/direct rule application). By the way, I have mostly self-taught myself Isabelle from these books without any prior experience in formal methods.
Isar has the new way of assumes ... shows ... for stating the theorem.
Is there similar support for proving iff's (⟷) like the example above?
Without it, there is no access to assms etc., and is it necessary to
assume everything except the conclusion during the proof.
I will make the advice given by Dominique Unruh more explicit: use rule iffI or intro iffI for this.
Edit. When you use rule iffI (or similar) to start your structured Isar proof, you need to state your assumptions explicitly for every subgoal (using the assume ... show ... paradigm). However, there is a tool that can generate such boilerplate Isar code automatically. It is called Sketch-and-Explore and you can find it in the directory HOL/ex of the main library of Isabelle/HOL. In this case, all you need to do is to type sketch(rule iffI) and the assume/show paradigm will be generated automatically for every subgoal.
References
Avigad J, Lewis RY, and van Doorn F. Logic and Proof.
Jech T. Set theory. 3rd ed. Heidelberg: Springer; 2006. (Pure and applied mathematics, a series of monographs and textbooks).
Kunčar O, Popescu A. From Types to Sets by Local Type Definition in Higher-Order Logic. Journal of Automated Reasoning. 2019;62(2):237–60.
Immler F, Zhan B. Smooth Manifolds and Types to Sets for Linear Algebra in Isabelle/HOL. In: 8th ACM SIGPLAN International Conference on Certified Programs and Proofs. New York: ACM; 2019. p. 65–77. (CPP 2019).
Nipkow T, Klein G. Concrete Semantics with Isabelle/HOL. Heidelberg: Springer-Verlag; 2017. (http://concrete-semantics.org/)
Nipkow T, Paulson LC, Wenzel M. A Proof Assistant for Higher-Order Logic. Heidelberg: Springer-Verlag; 2017.

Isabelle: proof obligation - proving using counterexamples

For an example lemma like this:
lemma someFuncLemma: "∀ (e::someType) . pre_someFunc 2 e"
which gives the following when using quickcheck:
Auto Quickcheck found a counterexample:
e = - 1
or when using Nitpick (which isn't really the main point here):
Nitpick found a counterexample:
Skolem constant:
e = - 1
How can I then use this counterexample to finish the proof?
As you can see, I'm not very familiar with Isabelle and POs.
Thank you for your help!
The presence of a counterexample usually indicates that you won't be able to prove your proposition, except maybe
the counterexample is spurious;
the underlying logic is inconsistent.
I'm assuming you want to prove there exists some e such that pre_someFunc 2 e is false. You would have to change your lemma to use exists instead of forall, and prefix your predicate with not:
lemma "∃e::someType. ¬(pre_someFunc 2 e)"
Then you can provide the counter-example using rule exI[where x=...] which sets the free variable x in exI to something. You can look at the definition of exI and how x is used by clicking it while holding Ctrl in Isabelle JEdit.
A simple example:
lemma "∃n :: nat. ¬ odd n"
apply (rule exI[where x=2])
apply simp
done

Apply simplifier to arbitrary term

I have a term in mind, say "foo 1 2 a b", and I'd like to know if Isabelle can simplify it for me. I'd like to write something like
simplify "foo 1 2 a b"
and have the simplified term printed in the output buffer. Is this possible?
My current 'workaround' is:
lemma "foo 1 2 a b = blah"
apply simp
which works fine but looks a bit hacky.
What doesn't work (in my case) is:
value "foo 1 2 a b"
because a and b are unbound variables, and because my foo involves infinite sets and other fancy stuff, which the code generator chokes on.
There is no built-in feature AFAIK, but there are several ways to achieve this. You have already discovered one of them, namely state the term as a lemma and then invoke the simplifier. The drawback is that this cannot be used in all contexts, for example, not inside an apply proof script.
Alternatively, you can invoke the simplifier via the attribute [simplified]. This works in all contexts via the thm command and produces the output in the output buffer. First, the term must be injected into a theorem, then you can apply simplify to the theorem and display the result with thm. Here is the preparatory stuff that can go into your theory of miscellaneous stuff.
definition simp :: "'a ⇒ bool" where "simp _ = True"
notation (output) simp ("_")
lemma simp: "simp x" by(simp add: simp_def)
Then, you can write
thm simp[of "foo 1 2 a b", simplified]
and see the simplified term in the output window.
The evaluation mechanism is probably not what you want, because evaluation uses a different set of rewrite rules (namely the code equations) than the simplifier normally uses (the simpset). Therefore, this is likely to evaluate to a different term than by applying the simplifier. To see the difference, apply code_simp instead of simp in your approach with lemma "foo 1 2 a b = blah". The proof method code_simp uses the code equations just like value [simp] used to.
When using the value command, the evaluation of the argument is conducted by registered evaluators (see the Reference Manual of Isabelle2013-2).
It used to be possible to explicitly choose an evaluator in previous versions of Isabelle (e.g., Isabelle2013-2) by giving an extra argument to the value command. E.g.,
value [simp] "foo 1 2 a b"
It seems that in Isabelle2014 this parameter was dropped and according to the Reference Manual of Isabelle2014, the strategy is now fixed to first use ML code generation and in case this fails, normalization by evaluation.
From the NEWS file in the development version (e82c72f3b227) of Isabelle it seems as if this parameter will be enabled again in the upcoming Isabelle release.
UPDATE: As Andreas pointed out, value [simp] does not use the same set of simplification rules as apply simp. So even if available, the solution I described above will most likely not yield the result you want.

Resources