Isabelle "Failed to apply proof method" when working with two theory files - isabelle

I have theory file Test_Func.thy which I have copied in Isabelle src/HOL and which defines function add_123:
theory Test_Func
imports Main
begin
fun add_123 :: "nat ⇒ nat ⇒ nat" where
"add_123 0 n = n" |
"add_123 (Suc m) n = Suc(add_123 m n)"
end
And then I have Test_1.thy file which have import and lemma:
theory Test_1
imports Main "HOL.Test_Func"
begin
lemma add_02: "add_123 m 0 = m"
apply(simp)
done
end
The strange thing is that apply(simp) or apply(auto) fails with Failed to apply proof method. There is no error message about undefined function or unvisible function, but somehow such simple proof is not working when function definition and lemma about it is split into two files.
So - this question can have different problems and different solutions - maybe it is about my inexperience to import theory file or maybe I am confused about tactic choice and application.
I am observing this in jEdit of Isabelle 2021, but in different setting I can see that the same thing happening in Isabelle 2020 as well.

There is no need to put theory files into the Isabelle distribution (on the contrary, I'd better keep it intact to make sure your development can be used on other machines without touching Isabelle installation).
The issue with the failing proof lies in a different area: the definition of add_123 is inductive on the first argument and has no immediate rule how to handle the expression specified in lemma_02. (E.g., lemma add_01: "add_123 0 m = m" could be proved the way you used because it matches the first case specified in the definition.)
The solution is to use a proof by induction on the first argument:
apply (induction m)
apply simp_all
done
or, in short by (induction m) simp_all.

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.

How to resolve Isabelle termination _dom error message (avoiding recursive definition of two-argument max)?

I am trying to define my own simple max function in Isabelle and prove its termination:
fun two_integer_max_case :: "nat ⇒ nat ⇒ nat" where
"two_integer_max_case a b = (case a > b of True ⇒ a | False ⇒ b)"
termination by auto
But there is un-handled goal in the termination proof:
proof (prove)
goal (1 subgoal):
1. All two_integer_max_case_dom
Ignoring duplicate rewrite rule:
two_integer_max_case ?a1 ?b1 ≡ case ?b1 < ?a1 of True ⇒ ?a1 | False ⇒ ?b1
Duplicate fact declaration "Max_Of_Two_Integers.two_integer_max_case.simps" vs. "Max_Of_Two_Integers.two_integer_max_case.simps"⌂
Failed to finish proof⌂:
goal (1 subgoal):
1. ⋀a b. two_integer_max_case_dom (a, b)
I am focused specifically on message:
Failed to finish proof⌂:
goal (1 subgoal):
1. ⋀a b. two_integer_max_case_dom (a, b)
What does it mean? What is required of me? This ..._dom condition. Where I can read about that?
I have read Chapters 3.5 of https://isabelle.in.tum.de/dist/Isabelle2021/doc/tutorial.pdf and now I am reading Chapter 8.1 of https://isabelle.in.tum.de/dist/Isabelle2021/doc/functions.pdf about domain predicates. Well, I hope that I manage find the solution.
I am aware that things would be easy (at least in the termination proof) if I had manage to come up with the recursive definition of my max function (two natural type arguments). But I have not managed to find such definition (I guess that there is some creative definition in the base theories already) and I am not sure whether I would be happy with recursive definition because my intention is to generate the Haskell or Scala code from this my function and I would prefer that this code would not be recursive but that it would use the standard less, equality operators of respective languages.
Well - it my be problem for the code synthesis with Isabelle generally - if Isabelle prefers recursive definitions of the algorithmic constructions for which industrial-programming-style (whatever it be) is not requiring recursion, then the generated code can be of less maintainability and human comprehension (still an issue before the AI has taken the field of program synthesis).
There is no need to prove termination when using fun, since this Isabelle-command only accepts a function definition if it can prove termination automatically. Hence your termination-command is not necessary at all and actually confuses the system, since it has already proven termination.
Only when using function instead of fun, then you need to prove termination manually afterwards.
Hope this helps, René

How to obtain witness instances outside a lemma in Isabelle/HOL

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.

How do I state a lemma that does not respect sort constraints (for using OFCLASS)

How can I state a lemma in Isabelle/HOL that does not obey sort constraints?
To explain why that makes sense, consider the following example theory:
theory Test
imports Main
begin
class embeddable =
fixes embedding::"'a ⇒ nat"
assumes "inj embedding"
lemma OFCLASS_I:
assumes "inj (embedding::'a⇒_)"
shows "OFCLASS('a::type,embeddable_class)"
apply intro_classes by (fact assms)
instantiation nat :: embeddable begin
definition "embedding = id"
instance
apply (rule OFCLASS_I) unfolding embedding_nat_def by simp
end
end
This theory defines a type class "embeddable" with one type parameters "embedding". (Basically, the class embeddable characterizes countable numbers with an explicit enumeration, but I chose this simply to have a very simple example.)
To simplify instance-proofs for the type class, the theory states an auxiliary lemma OFCLASS_I that shows goals of the form OFCLASS(...,embeddable). This lemma can then be used to solve the proof obligations produced by instance.
First, why would I even want that? (In the present theory, apply (rule OFCLASS_I) does the same as the builtin apply intro_classes and hence is useless.) In more complex cases, there are two reasons for such a lemma:
The lemma can give alternative criteria for instantiation of a type class, making subsequent proofs simpler.
The lemma could be used in automated (ML-level) instantiations. Here using intro_classes is problematic, because the number of subgoals of intro_classes can vary depending on what instance proofs have been performed earlier. (A lemma such as OFCLASS_I has a stable, well-controlled subgoals.)
However, the above theory does not work in Isabelle/HOL (neither 2015 or 2016-RC1) because the lemma does not type-check. The sort constraint "embedding::(_::embeddable => _)" is not satisfied. However, this type contraint is not a logic necessity (it is not enforced by the logical kernel but instead of a higher level).
So, is there a clean way of stating the theory above? (I give a somewhat ugly solution in my answer, but I am looking for something cleaner.)
The checking of the sort constraints can be disabled temporarily on the ML-level, and reactivated afterwards. (See the complete example below.) But that solution looks very much like a hack. We have to change the sort constraints in the context, and remember to restore them afterwards.
theory Test
imports Main
begin
class embeddable =
fixes embedding::"'a ⇒ nat"
assumes "inj embedding"
ML {*
val consts_to_unconstrain = [#{const_name embedding}]
val consts_orig_constraints = map (Sign.the_const_constraint
#{theory}) consts_to_unconstrain
*}
setup {*
fold (fn c => fn thy => Sign.add_const_constraint (c,NONE) thy) consts_to_unconstrain
*}
lemma OFCLASS_I:
assumes "inj (embedding::'a⇒_)"
shows "OFCLASS('a::type,embeddable_class)"
apply intro_classes by (fact assms)
(* Recover stored type constraints *)
setup {*
fold2 (fn c => fn T => fn thy => Sign.add_const_constraint
(c,SOME (Logic.unvarifyT_global T)) thy)
consts_to_unconstrain consts_orig_constraints
*}
instantiation nat :: embeddable begin
definition "embedding = id"
instance
apply (rule OFCLASS_I) unfolding embedding_nat_def by simp
end
end
This theory is accepted by Isabelle/HOL. The approach works in more complex settings (I have used it several times), but I would prefer a more elegant one.
The following is a different solution that does not require to "clean up" after proving the lemma (this answer requires to "repair" the sort constraints afterwards).
The idea is to define a new constant (embedding_UNCONSTRAINED in my example) that is a copy of the original sort-constrained constant (embedding), except without sort constraints (using the local_setup command below). Then the lemma is stated using embedding_UNCONSTRAINED instead of embedding. But by adding the attribute [unfolded embedding_UNCONSTRAINED_def] the lemma that is actually stored uses the constant embedding without sort constraint.
The drawback of this approach is that during the proof of the lemma, we can never explicitly write any terms containing embedding (since that will add the unwanted sort constraints). But if we need to state a subgoal containing embedding, we can always state it using embedding_UNCONSTRAINED and then use, e.g., unfolding embedding_UNCONSTRAINED to transform it into embedding.
theory Test
imports Main
begin
class embeddable =
fixes embedding::"'a ⇒ nat"
assumes "inj embedding"
(* This does roughly:
definition "embedding_UNCONSTRAINED == (embedding::('a::type)=>nat)" *)
local_setup {*
Local_Theory.define ((#{binding embedding_UNCONSTRAINED},NoSyn),((#{binding embedding_UNCONSTRAINED_def},[]),
Const(#{const_name embedding},#{typ "'a ⇒ nat"}))) #> snd
*}
lemma OFCLASS_I [unfolded embedding_UNCONSTRAINED_def]:
assumes "inj (embedding_UNCONSTRAINED::'a⇒_)"
shows "OFCLASS('a::type,embeddable_class)"
apply intro_classes by (fact assms[unfolded embedding_UNCONSTRAINED_def])
thm OFCLASS_I (* The theorem now uses "embedding", but without sort "embeddable" *)
instantiation nat :: embeddable begin
definition "embedding = id"
instance
apply (rule OFCLASS_I) unfolding embedding_nat_def by simp
end
end

Isabelle won't generate code for my recursive function without a termination proof

If I write a non-trivial recursive function (i.e. using function rather than fun), then the code generator refuses to execute my function unless I supply a termination proof -- even a sorry-proof suffices. Here is my minimal working example (don't worry about the contents of the foo function, it's just a random function with a non-trivial termination proof):
theory Misc imports
Main
"~~/src/HOL/Library/Code_Target_Numeral"
begin
function foo :: "nat list ⇒ nat list list"
where
"foo G = (
if G = [] then
map concat [[G]]
else
concat (map foo (map (λx. [Suc x]) (tl G)))
)"
by auto
termination sorry
value "foo [1,2,3]"
end
Currently the value command returns the correct result "[]". But if I remove the line termination sorry, then the value command returns "foo [1, 2, 3]".
Is there any way to make the code generator execute foo, without having to spoil my theory file with the horrible sorry? I don't want to actually do the termination proof because it's a really hard proof.
Chris' comment answers your question, but I thought I would just give an example.
A non-terminating function has the ability to introduce unsoundness, which is why a proof of termination is required (or at very least a sorry).
For example, with the following function definition:
function f :: "nat ⇒ nat" where "f n = f n + 1"
by auto
termination
sorry
we can then go on to prove False:
lemma "False"
by (metis a.f.elims add_eq_self_zero if_1_0_0)
The unsoundness comes from the non-termination of the function.
In your concrete example, the termination proof isn't too hard: you can see that the length of the list G is always decreasing. We can then just use a measure function and show that it decreases, as so:
termination
(* Give Isabelle a termination relation. We say that the length
* of the parameter is strictly decreasing to 0. *)
apply (relation "measure length", simp)
(* simplify *)
apply clarsimp
(* Sledgehammer can now solve this goal, but by hand is a bit neater. *)
apply (drule length_pos_if_in_set, clarsimp)
done

Resources