For a uni project I'm working on a proof with Iabelle/HOL 2018. I'm getting an error when applying obvious results. However, this error is not stating anything about what is going wrong.
At first I thought it was an unification problem. But when I simplified it turned out to be a behavior I totally don't understand.
I have a minimal example which is as follows:
I define proposition formulas as type1 and then I have a tail-recursive function that simply collects each sub formula. There are probably better ways to do that. I just tried to replicate the error in the easiest way possible. Then I want to show a simple equality (I have proven that in my code, here I just simplify by "sorry") and then I want to use that fact in some other proof, however it doesn't seem to apply the proven fact, even though I added it to the simp set. Even, directly applying it doesn't work for me.
Here is the Code:
theory test
imports Main
begin
datatype 'a type1 =
Bot
| Atm 'a
| Neg "'a type1"
| Imp "'a type1" "'a type1"
fun func :: "'a type1 ⇒ ('a type1) list list ⇒ ('a type1) list list"
where
"func Bot acc = acc"
| "func (Atm p) acc = acc"
| "func (Neg p) acc = func p ([Neg p] # acc)"
| "func (Imp p q) acc = func q (func p ([Imp p q] # acc))"
lemma lemma1 [simp]:
"func p acc = func p [] # acc"
sorry
lemma lemma2:
"func p acc = func p acc"
proof -
have "func p acc = func p [] # acc" by auto
show ?thesis sorry
qed
end
In my opinion this should be no problem. However, in the first line of the proof of lemma2 I get an error. But there is no explanation to the error such as "failed to finish proof" or anything similar.
Does anyone know what I'm doing wrong? Or did anyone have similar problems or behavior?
Quoting from the book 'A Proof Assistant for Higher-Order Logic': "In its most basic form, simplification means the repeated application of equations from left to right ... Only equations that really simplify, like rev (rev xs) = xs and xs # [] = xs, should be declared as default simplification rules." (there are other valuable resources that explain this issue, e.g. the official Isabelle/Isar reference manual or the textbook 'Concrete Semantics with Isabelle/HOL'). Therefore, lemma1 is not a good choice for a default simplification rule and adding it to the simpset can lead to nontermination, as your example demonstrates.
If you would like to use lemma1 in another proof, perhaps, you can use something similar to
have "func p acc = func p [] # acc by (rule lemma1)"
or merely rewrite the simp rule as
func p [] # acc = func p acc.
However, in general, you need to be very careful when introducing new simp rules, especially in the global theory context.
Related
I'm currently trying use Isabelle/HOL's reification tactic. I'm unable to use different interpretation functions below quantifiers/lambdas. The below MWE illustrates this. The important part is the definition of the form function, where the ter call occurs below the ∀. When trying to use the reify tactic I get an Cannot find the atoms equation error. I don't get this error for interpretation functions which only call themselves under quantifiers.
I can't really reformulate my problem to avoid this. Does anybody know how to get reify working for such cases?
theory MWE
imports
"HOL-Library.Reflection"
begin
datatype Ter = V nat | P Ter Ter
datatype Form = All0 Ter
fun ter :: "Ter ⇒ nat list ⇒ nat"
where "ter (V n) vs = vs ! n"
| "ter (P t1 t2) vs = ter t1 vs + ter t2 vs"
fun form :: "Form ⇒ nat list ⇒ bool"
where "form (All0 t) vs = (∀ v . ter t (v#vs) = 0)" (* use of different interpretation function below quantifier *)
(*
I would expect this to reify to:
form (All0 (P (V 0) (V 0))) []
instead I get an error :-(
*)
lemma "∀ n :: nat . n + n = 0"
apply (reify ter.simps form.simps)
(* proof (prove)
goal (1 subgoal):
1. ∀n. n + n = n + n
Cannot find the atoms equation *)
oops
(* As a side note: the following example in src/HOL/ex/Reflection_Examples.thy (line 448, Isabelle2022) seems to be broken? For me, the reify invocation
doesn't change the goal at all. It uses quantifiers too, but only calls the same interpretation function under quantifiers and also doesn't throw an error,
so at least for me this seems to be unrelated to my problem.
*)
(*
lemma " ∀x. ∃n. ((Suc n) * length (([(3::int) * x + f t * y - 9 + (- z)] # []) # xs) = length xs) ∧ m < 5*n - length (xs # [2,3,4,x*z + 8 - y]) ⟶ (∃p. ∀q. p ∧ q ⟶ r)"
apply (reify Irifm.simps Irnat_simps Irlist.simps Irint_simps)
oops
*)
end
I am trying to use code_pred for an inductive predicate defined inside a locale. I came across this email which shows how this can be done:
locale l = fixes x :: 'a assumes "x = x"
inductive (in l) is_x where "is_x x"
global_interpretation i: l "0 :: nat" defines i_is_x = "i.is_x" by unfold_locales simp
declare i.is_x.intros[code_pred_intro]
code_pred i_is_x by(rule i.is_x.cases)
However, when I change the global_interpretation to use () :: unit instead of 0 :: nat, then code_pred fails with the following message:
Tactic failed
The error(s) above occurred for the goal statement⌂:
i_is_x_i x = Predicate.bind (Predicate.single x) (λx. case x of () ⇒ Predicate.single ())
I tried to do the prove manually, but at some point I got the same error.
Does anyone know how to solve this?
I don't exactly understand what is happening here, but the code generator already uses unit internally and the tactic (corresponding to before single intro rule) fails on your example.
The error does not come from the proof, but comes from proof done internally in the done part. So changing the proof does not change the issue (and you even see with sorry).
A possible work-around: use a type isomorphic to unit:
datatype myunit = MyUnit
locale l = fixes x :: 'a assumes "x = x"
inductive (in l) is_x where "is_x x"
definition X where ‹X = ()›
global_interpretation i: l "MyUnit :: myunit" defines i_is_x = "i.is_x" by unfold_locales simp
declare i.is_x.intros[code_pred_intro]
code_pred i_is_x
by (rule i.is_x.cases)
I am trying to write a function in Isabelle for some tasks in an assignment
and I wanted to make sure that my function works correctly so I thought about
testing it in SML but I can't seem to be able to figure out how to write it. I
have never used/wrote/learned functional programming so I am having a little trouble
with it. Can someone help me or maybe if there is something in Isabelle about testing
how a function works can he point me to the correct direction?
The functions are the following and basically are removing the first occurrence of an
element in a list and removing all occurrences from a list
fun del1:: "'a ⇒ 'a list ⇒ 'a list" where
"del1 a Nil = Nil" |
"del1 a (x#xs) = (if x = a then xs else x#(del1 a xs))"
fun delall:: "'a ⇒ 'a list ⇒ 'a list" where
"delall a Nil = Nil" |
"delall a (x#xs) = (if x = a then (delall a xs) else x#(delall a xs))"
I am not quite sure whether I get your question. First, the syntax of Isabelle/HOL and SML is not very different. If you just want to get variants of your function in SML, probably the fastest way is using Isabelle's code generator
export_code del1 delall in SML
which results in something like
fun del1 A_ a [] = []
| del1 A_ a (x :: xs) = (if HOL.eq A_ x a then xs else x :: del1 A_ a xs);
fun delall A_ a [] = []
| delall A_ a (x :: xs) =
(if HOL.eq A_ x a then delall A_ a xs else x :: delall A_ a xs);
However, since this contains some Isabelle specific stuff (like a dictionary construction to get rid of type classes) maybe you prefer hand-written code:
fun del1 a [] = []
| del1 a (x::xs) = if x = a then xs else x :: (del1 a xs)
fun delall a [] = []
| delall a (x::xs) = if x = a then delall a xs else x :: delall a xs
On the other hand, if you just want to try out your functions on some inputs, you can do that inside Isabelle using value. E.g.,
value "del1 (2::nat) [1, 2 , 3]"
value "delall ''x'' [''x'', ''y'', ''z'', ''q'', ''x'']"
There is little to add to the answer by Chris, only this detail: In Isabelle2013-2 the Isabelle/jEdit Documentation panel has a few quickstart examples. This includes src/HOL/ex/ML.thy which shows a little bit of Isabelle/HOL as logical language and SML as functional language, and some connections between them.
I am not sure, but I think sometimes my proofs would be easier if I had a predecessor function, e.g., in case a variable is known not to be zero.
I don't know a good example, but perhaps here: { fix n have "(n::nat) > 0 ⟹ (∑i<n. f i) = Predecessor n" sorry }
Possibly because it is not a good idea, there is no predecessor function in the library.
Is there a way to simulate a predecessor function or similar?
I have thought of this example:
theorem dummy:
shows "1=1" (* dummy *)
proof-
(* Predecessor function *)
def pred == "λnum::nat. (∑i∈{ i . Suc i = num}. i)"
{fix n :: nat
from pred_def have "n>0 ⟹ Suc (pred n) = n"
apply(induct n)
by simp_all
}
show ?thesis sorry
qed
Your definition is unnecessarily complicated. Why do you not just write
def pred ≡ "λn::nat. n - 1"
Then you can have
have [simp]: "⋀n. n > 0 ⟹ Suc (pred n) = n" by (simp add: pred_def)
In the case of 0, the pred function then simply returns 0 and Suc (pred 0) = 0 obviously doesn't hold. You could also define pred ≡ "λn. THE n'. Suc n' = n". That would return the unique natural number whose successor is n if such a number exists (i.e. if n > 0) and undefined (i.e. some natural number you know nothing about) otherwise. However, I would argue that in this case, it is much easier and sensible to just do pred ≡ λn::nat. n - 1.
I would suspect that in most cases, you can simply forgo the pred function and write n - 1; however, I do know that it is sometimes good to have the - 1 “protected” by a definition. In these cases, I usually def a variable n' as n - 1 and prove Suc n' = n – basically the same thing. In my opinion, seeing as proving this takes only one line, it does not really merit a definition of its own, such as this pred function, but one could make a reasonable case for it, I guess.
Another thing: I've noticed you use lemma "1 = 1" as some kind of dummy environment to do Isar proofs in. I would like to point out the existence of notepad, which exists precisely for that use case and that can be used as follows:
notepad
begin
have "some fact" by something
end
I want to match and remove those elements in l :: 'a list that match the predicate P :: ('a => bool)
What is the best way to accomplish such a task? How can I find out about existing functions that might help me?
One way to find functions you expect to exist is the document What's in Main from the Isabelle documentation. It gives a quick overview of the main types, functions and syntax provided by the theory Main of Isabelle/HOL.
If you look at the List section in ths document, you find the function filter which seems to have the correct type.
Short Story: Use find_consts
Long Story:
This a How-To to conquer such problems.
In Main, there is List.dropWhile
List.dropWhile :: "('a => bool) => 'a list => 'a list"
However, it only removes from the beginning. This may not be the intended function.
value "List.dropWhile (λ x. x = ''c'') [''c'', ''c'', ''d'']"
"[''d'']"
value "List.dropWhile (λ x. x = ''c'') [''d'', ''c'', ''c'']"
"[''d'', ''c'', ''c'']"
Manual Approach
We can write a function ourselves which removes all occurrences
fun dropAll :: "('a => bool) => 'a list => 'a list" where
"dropAll P [] = []"
| "dropAll P (x # xs) = (if P x then dropAll P xs else x # (dropAll P xs))"
Searching the Library
However, this function is equivalent to filtering with ¬ P
How can we find such library functions?
If we know the signature of what we want to do, we can use find_consts
find_consts "('a ⇒ bool) ⇒ 'a list ⇒ 'a list"
It returns 3 functions from Main, with that signature: List.dropWhile, List.filter, List.takeWhile
Now, let's show that we don't need dropAll but can do the same with filter.
lemma "dropAll P l = filter (λ x. ¬ P x) l"
apply(induction l)
by simp_all
It is advisable not to implement things like dropAllyourself but rather use filter. Thus, all lemmata proven for filter are usable.
Hints
Hint: we can use the convenient list comprehension syntax to write e.g. filter expressions
lemma "filter (λ x. ¬ P x) l = [x ← l. ¬ P x]" by simp