From two sets in Isabelle a third list needs to be created with element of the form (a, b) where a is from the first set and b is in the second set. in addition the elements in the last set must be filtered by some condition.
The code:
theory Scratch
imports Main Nat
begin
value "let a = {(1::int), 2, 3, 4} in (let b = {(6::int),7,8,9} in
((1::int), 6) ∈ set (filter (λ el . (snd el) < 8) [(n,m). n ∈ a ∧ m ∈ b]))"
end
The result I expected was True or False. the results was:
"(1, 6)
∈ set [u←if (1 = n ∨ 2 = n ∨ 3 = n ∨ 4 = n) ∧
(6 = m ∨ 7 = m ∨ 8 = m ∨ 9 = m)
then [(n, m)] else [] . snd u < 8]"
:: "bool"
Why does the result not evaluate to a True/False value?
Is it possible to write code where the filter functions is evaluated on a set and not list?
First of all, you cannot convert sets to lists. Lists have a specific order of elements; sets do not.
Question 1
This is because you have free variables in there: n and m. The expression [(n,m). n ∈ a ∧ m ∈ b] basically means if n ∈ a ∧ m ∈ b then [(n,m)] else []. This is not what you want.
If a and b were lists, you could use the list comprehension syntax [(n,m). n ← a, m ← b]. However, since a and b are sets, this cannot possibly work, since the result would be a list with a specific order, and that order has to come from somewhere – but a and b, as sets, have no such order.
Question 2
In formalisation, the best approach is to first define things abstractly, without using data structures that are too concrete. If you don't need to maintain a specific ordering of your values, use a set, not a list. You can then later refine this from sets to lists in order to obtain executable (and efficient) code.
There is a section on refinement in the Isabelle code generation manual. I recommend you have a look at it.
That said, there is some limited support for code generation with sets. Sets are then internally represented as lists and most basic operations work, but code generation may sometimes fail – not all operations on sets are computable in general. There is the function Set.filter, which is executable and basically does the same on sets as the regular filter function does for lists.
However, the following will fail due to a wellsortedness error:
value "let a = {(1::int), 2, 3, 4} in (let b = {(6::int),7,8,9} in
((1::int), (6 :: int)) ∈ Set.filter (λ el . (snd el) < 8) {x. fst x ∈ a ∧ snd x ∈ b})"
This is because set comprehensions (i.e. {x. … }) are, in general, not computable. You have to replace this {x. fst x ∈ a ∧ snd x ∈ b} with something the code generator can generate code for. In this case, it's easy, because this operation is just the cartesian product. You can write:
value "let a = {(1::int), 2, 3, 4} in (let b = {(6::int),7,8,9} in
((1::int), (6 :: int)) ∈ Set.filter (λ el . (snd el) < 8) (a × b))"
And you get the result you'd expect.
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 have a beginner's question about Isabelle/HOL:
I want to prove the following lemma:
lemma
shows "{(x,y) . x ∈ {0..<n} ∧ y ∈ {0..<n} ∧ x = y} = {(x,x). x < n}"
But the proof state is:
proof (prove)
goal (1 subgoal):
1. {(x, y). x ∈ {0..<n} ∧ y ∈ {0..<n} ∧ x = y} = {(xa, x). x < n}
Why did the xa appear and how can I define the set the right (succinct) way?
The (x,y) in the set comprehension {(x,y). ....} is binding variable names. As you write {(x,x). x < n}, you bind two variables named x, where the second x shadows the first.
{(x,x). x < n} is just a nice syntax for a lambda term, actually. Internally, it translates to Collect (case_prod (λx. λx. x < n)). Looking at the term this way, the shadowing is more obvious.
To fix your problem, you have to explicitly express the information that the first and the second bound variable are to be identical, that is: {(x1,x2). x1 = x2 ∧ x1 < n}.
As a side note: The lemma you are trying to show is not true. (For example, n could be an int.) If you want n to be a nat, you have to make this explicit, for example by giving a type in your goal like this {(x,y). x ∈ {0..<(n::nat)} ∧ y ∈ {0..<n} ∧ x = y} = {(x1,x2). x1 = x2 ∧ x1 < n}.
Especially, if you are a beginner, I would strongly suggest to introduce free variables in lemma heads explicitly with the syntax lemma Name: fixes n :: ‹nat› assumes ‹...› shows ‹...›.
I need to generate a code calculating all values greater or equal to some value:
datatype ty = A | B | C
instantiation ty :: order
begin
fun less_ty where
"A < x = (x = C)"
| "B < x = (x = C)"
| "C < x = False"
definition "(x :: ty) ≤ y ≡ x = y ∨ x < y"
instance
apply intro_classes
apply (metis less_eq_ty_def less_ty.elims(2) ty.distinct(3) ty.distinct(5))
apply (simp add: less_eq_ty_def)
apply (metis less_eq_ty_def less_ty.elims(2))
using less_eq_ty_def less_ty.elims(2) by fastforce
end
instantiation ty :: enum
begin
definition [simp]: "enum_ty ≡ [A, B, C]"
definition [simp]: "enum_all_ty P ≡ P A ∧ P B ∧ P C"
definition [simp]: "enum_ex_ty P ≡ P A ∨ P B ∨ P C"
instance
apply intro_classes
apply auto
by (case_tac x, auto)+
end
lemma less_eq_code_predI [code_pred_intro]:
"Predicate_Compile.contains {z. x ≤ z} y ⟹ x ≤ y"
(* "Predicate_Compile.contains {z. z ≤ y} x ⟹ x ≤ y"*)
by (simp_all add: Predicate_Compile.contains_def)
code_pred [show_modes] less_eq
by (simp add: Predicate_Compile.containsI)
values "{x. A ≤ x}"
(* values "{x. x ≤ C}" *)
It works fine. But the theory looks over-complicated. Also I can't calculate values less or equal to some value. If one will uncoment the 2nd part of less_eq_code_predI lemma, then less_eq will have only one mode i => i => boolpos.
Is there a simpler and more generic approach?
Can less_eq support i => o => boolpos and o => i => boolpos at the same time?
Is it possible not to declare ty as an instance of enum class? I can declare a function returning a set of elements greater or equal to some element:
fun ge_values where
"ge_values A = {A, C}"
| "ge_values B = {B, C}"
| "ge_values C = {C}"
lemma ge_values_eq_less_eq_ty:
"{y. x ≤ y} = ge_values x"
by (cases x; auto simp add: dual_order.order_iff_strict)
This would allow me to remove enum and code_pred stuff. But in this case I will not be able to use this function in the definition of other predicates. How to replace (≤) by ge_values in the following definition?
inductive pred1 where
"x ≤ y ⟹ pred1 x y"
code_pred [show_modes] pred1 .
I need pred1 to have at least i => o => boolpos mode.
The predicate compiler has an option inductify that tries to convert functional definitions into inductive ones. It is somewhat experimental and does not work in every case, so use it with care. In the above example, the type classes make the whole situation a bit more complicated. Here's what I managed to get working:
case_of_simps less_ty_alt: less_ty.simps
definition less_ty' :: "ty ⇒ ty ⇒ bool" where "less_ty' = (<)"
declare less_ty_alt [folded less_ty'_def, code_pred_def]
code_pred [inductify, show_modes] "less_ty'" .
values "{x. less_ty' A x}"
The first line convertes the pattern-matching equations into one with a case expression on the right. It uses the command case_of_simps from HOL-Library.Simps_Case_Conv.
Unfortunately, the predicate compiler seems to have trouble with compiling type class operations. At least I could not get it to work.
So the second line introduces a new constant for (<) on ty.
The attribute code_pred_def tells the predicate compiler to use the given theorem (namely less_ty_alt with less_ty' instead of (<)) as the "defining equation".
code_pred with the inductify option looks at the equation for less_ty' declared by code_pred_def and derives an inductive definition out of that. inductify usually works well with case expressions, constructors and quantifiers. Everything beyond that is at your own risk.
Alternatively, you could also manually implement the enumeration similar to ge_values and register the connection between (<) and ge_values with the predicate compiler. See the setup block at the end of the Predicate_Compile theory in the distribution for an example with Predicate.contains. Note however that the predicate compiler works best with predicates and not with sets. So you'd have to write ge_values in the predicate monad Predicate.pred.
My attempt to create a custom linear order for a custom data type failed, Below is my code:
theory Scratch
imports Main
begin
datatype st = Str "string"
fun solf_str_int:: "string ⇒ int" where
"solf_str_int str = (if (size str) > 0
then int(nat_of_char (hd str) + 1) + 100 * (solf_str_int (tl str))
else 0)"
fun soflord:: "st ⇒ st ⇒ bool" where
"soflord s1 s2 = (case s1 of Str ss1 ⇒ (case s2 of Str ss2 ⇒
(solf_str_int ss1) ≤ (solf_str_int ss2)))"
instantiation st :: linorder
begin
definition nleq: "less_eq n1 n2 == soflord n1 n2"
definition neq: "eq n1 n2 == (n1 ≤ n2) ∧ (n2 ≤ n1)"
definition nle: "less n1 n2 == (n1 ≤ n2) ∧ (¬(n1 = n2))" (* ++ *)
instance proof
fix n1 n2 x y :: st
show "n1 ≤ n1" by (simp add:nleq split:st.split)
show "(n1 ≤ n2) ∨ (n2 ≤ n1)" by (simp add:nleq split:st.split) (*why is 'by ()' highlited?*)
(*this fail if I comment line ++ out*)
show "(x < y) = (x ≤ y ∧ (¬ (y ≤ x)))" by (simp add:nleq neq split:node.split)
qed
end
end
The definition marked with (* ++ *) is not right and if delete it the last show give problems.
How do I correct the prove?
Why is the second last show partially highlighted?
When you define the operations of a type class (less_eq and less in the case of linorder), the name of the overloaded operation can only be used if the inferred type of the operation matches exactly the overloaded instance that is being defined. In particular, the type is not specialised if it turns out to be too general.
The definition for less_eq works because soflord restricts the types of n1 and n2 to st, so less_eq is used with type st => st => bool, which is precisely what is needed here. For less, type inference computes the most general type 'b :: ord => 'b => bool. As this is not of the expected type st => st => bool, Isabelle does not recognize the definition as a definition of an overloaded operation and consequently complains that you want to redefine an existing operation in its full generality. If you restrict the types as necessary, then the definition works as expected.
definition nle: "less n1 (n2 :: st) == (n1 ≤ n2) ∧ (¬(n1 = n2))"
However, your definitions do not define a linear order on st. The problem is that antisymmetry is violated. For example, the two strings Str ''d'' and Str [Char Nibble0 Nibble0, Char Nibble0 Nibble0] (i.e., the string consisting of two characters at codepoint 0) are "equivalent" in your order, although they are different values. You attempt to define equality on st, too, but in higher-order logic, equality cannot be defined. It is determined by the way you constructed your type. If you really want to identify strings that are equivalent according to your order, you have to construct a quotient first, e.g., using the quotient package.
The purple highlighting of by(simp ...) indicates that the proof method simp is still running. In your case, it will not terminate, because simp will keep unfolding the defining equation for solf_str_int: its right-hand side contains an instance of the left-hand side. I recommend that you define your functions by pattern-matching on the left-hand side of =. Then, the equations are only used when they can consume a pattern. Thus, you have to trigger case distinctions yourself (e.g. using cases), but you also get more control over the automated tactics.
I have the following recursive function that creates a list of 0s (i.e. [0,...,0]) in VDM. How can this be translated to Isabelle using fun-where?
VDM:
NewList: nat1 * seq of nat -> seq of nat
NewList(n, l) ==
if len l = n then l
else NewList(n, l ^ [0])
-- pre/post-conditions excluded here
My attempts are horribly wrong due to my lack of understanding of Isabelle (but below at least proves that I tried...).
Isabelle:
fun
NewList:: "N ⇒ (VDMNat VDMSeq) ⇒ (VDMNat VDMSeq)"
where
"NewList n [] = NewList n [0]"
| "NewList n [x] = (if len [x] = n then [x] else NewList n (x#[0]))"
| "NewList n (x # xs) = (if len (x # xs) = n then (x # xs) else NewList n ((x # xs) # [(0::VDMNat)]))"
*The data types VDMNat and VDMSeq are defined in some library. Please ignore the VDMNat and VDMSeq for now - any sort of implementation using Isabelle's data types are welcome (at least it would provide a good reference for my implementation). Please refer to the VDM code for the data types intended.
Could you also please explain what x, xs, and (x # xs) are referring to? I've seen this in several recursive function examples (though none helps me).
Thank you for your help!
First of all, x and xs are variables. When definiting recursive functions on lists, these are often used to denote the first element of the list (x) and the remaining list (xs). The expression x # xs means ‘x prepended to the list xs’, and that is the reason why (x # xs) # [0] in your question does not work: x # xs is a list and [0] is also a list. You would have to do x # xs # [0}, where # is the function to concatenate two lists.
Now, to your function: My interpretation of your function definition is that you have a natural number n and a list l and want to pad the list l with zeros at the back up to length n.
However, when the list l is of length > n to begin with, your function does not terminate. You would have to think about what to do in that case.
Here are my suggestions for what you could do:
Possibility 1
Change the = n to a ≥ n. Then you can prove termination of the function by looking at
function new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = (if length l ≥ n then l else new_list n (l # [0]))"
by pat_completeness auto
termination by (relation "measure (λ(n, l). n - length l)") auto
However, proving theorems about this will probably get ugly. I would therefore urge you to do something like the following two possibilities. Ideally, use functions from Isabelle's standard library, because there is usually good automation setup for them. Alternatively, define your own small building blocks (like take and replicate) for your datatypes and prove reusable facts on them and combine them to do what you want. A ‘monolithic’ function definition like yours is difficult to work with when doing proofs.
Possibility 2
Use the builtin function replicate, which takes a natural number n and an element and returns a list of n times that element:
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = l # replicate (n - length l) 0"
You can also do the same thing with fun, but definition is the more low-level tool. Note that definition does not add the function definition theorem new_list_def as a simplifier rule; you can do this by writing declare new_list_def [simp].
Possibility 3
You can combine possibility 2 with the builtin function take to ensure that you always get a list of length exactly n, even when the input list is longer (it is then possibly truncated):
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = take n l # replicate (n - length l) 0"
Summary
In the first two cases, you can prove the theorems
length l ≤ n ⟹ length (new_list n l) = n
take (length l) (new_list n l) = l
(in the first case by induction using new_list.induct; in the second case just by unfolding the definition and simplifying)
In the third case, you can prove
length (new_list n l) = n
take (length l) (new_list n l) = take n l
Obviously, if length l ≤ n, the first two and the last one coincide completely.
The easy solution is: replicate n (0::nat) using the function replicate of Isabelle/HOL's library.
If you want to implement the function yourself via fun then do what you should always do in functional programming ;) try to split your problem into smaller problems that can be solved recursively:
fun newlist :: "nat => nat list"
where
"newlist 0 = []" -- "the only list of length 0*)
| "newlist (Suc n) = ..." -- "use result for 'n' to obtain result for 'n+1'"