Isabelle 'fun' without 'where'? - isabelle

Isabelle doesn't let me write just fun f :: "nat list => nat"; I have to add at least one defining equation, e.g. where "f [] = 5". But since it's fine to leave some constructors undefined, why can't I simply leave all constructors undefined? Then, a fun f without a where would be a handy alternative to the consts f declaration.

Firstly, I do not see why fun f without where is a handy alternative to consts f. You have the latter at your disposal (but also have to specify a type), use it if you must. Moreover, consts is more primitive than commands like definition, fun, and -- in my opinion -- shouldn't be used in production code.
As for your comment on leaving constructors undefined inside a function declaration. The wording "undefined" is misleading here, since, e.g., after
fun f :: "'a list => nat"
where
"f [] = 0"
you can prove that
lemma "f (x#xs) = undefined"
by (cases "x#xs" rule: f.elims) simp
and thus f (x#xs) is, in some sense, defined to be the value undefined. In contrast, after
consts f :: "'a list => nat"
you cannot proof anything about f. You just told the system that there is a constant f of a certain type, but without saying anything further about it.

Related

Isabelle/HOL restrict codomain

I am sorry for asking so many Isabelle questions lately. Right now I have a type problem.
I want to use a type_synonym introduced in a AFP-theory.
type_synonym my_fun = "nat ⇒ real"
I have a locale in my own theory where:
fixes n :: nat
and f :: "my_fun"
and A :: "nat set"
defines A: "A ≡ {0..n}"
However, in my use case the output of the function f is always a natural number in the set {0..n}. I want to impose this as a condition (or is there a better way to do it?). The only way I found was to:
assumes "∀v. ∃ i. f v = i ∧ i ∈ A"
since
assumes "∀v. f v ∈ A"
does not work.
If I let Isabelle show me the involved types it seems alright to me:
∀v::nat. ∃i::nat. (f::nat ⇒ real) v = real i ∧ i ∈ (A::nat set)
But of course now I cannot type something like this:
have "f ` {0..10} ⊆ A"
But I have to prove this. I understand where this problem comes from. However, I do not know how to proceed in a case like this. What is the normal way to deal with it? I would like to use my_fun as it has the same meaning as in my theory.
Thank you (again).
If you look closely at ∀v::nat. ∃i::nat. (f::nat ⇒ real) v = real i ∧ i ∈ (A::nat set), you will be able to see the mechanism that was used for making the implicit type conversion between nat and real: it is the abbreviation real (this invokes of_nat defined for semiring_1 in Nat.thy) that appears in the statement of the assumption in the context of the locale.
Of course, you can use the same mechanism explicitly. For example, you can define A::real set as A ≡ image real {0..n} instead of A::nat set as A ≡ {0..n}. Then you can use range f ⊆ A instead of assumes "∀v. ∃ i. f v = i ∧ i ∈ A”. However, I doubt that there is a universally accepted correct way to do it: it depends on what exactly you are trying to achieve. Nonetheless, for the sake of the argument, your locale could look like this:
type_synonym my_fun = "nat ⇒ real"
locale myloc_basis =
fixes n :: nat
abbreviation (in myloc_basis) A where "A ≡ image real {0..n}"
locale myloc = myloc_basis +
fixes f :: "my_fun"
assumes range: "range f ⊆ A"
lemma (in myloc) "f ` {0..10} ⊆ A"
using range by auto
I want to impose this as a condition (or is there a better way to do
it?).
The answer depends on what is known about f. If only a condition on the range of f is known, as the statement of your question seems to suggest, then, I guess, you can only state is as an assumption.
As a side note, to the best of my knowledge, defines is considered to be obsolete and it is best to avoid using it in the specifications of a locale: stackoverflow.com/questions/56497678.

Reasoning about the entirety of a codatatype in Isabelle/HOL

I'd like to write down some definitions (and prove some lemmas!) about paths in a graph. Let's say that the graph is given implicitly by a relation of type 'a => 'a => bool. To talk about a possibly infinite path in the graph, I thought a sensible thing was to use a lazy list codatatype like 'a llist as given in "Defining (Co)datatypes and Primitively (Co)recursive Functions in Isabelle/HOL" (datatypes.pdf in the Isabelle distribution).
This works well enough, but I'd then I'd like to define a predicate that takes such a list and a graph relation and evaluates to true iff the list defines a valid path in the graph: any pair of adjacent entries in the list must be an edge.
If I was using 'a list as a type to represent the paths, this would be easy: I'd just define the predicate using primrec. However, the co-inductive definitions I can find all seem to generate or consume the data one element at a time, rather than being able to make a statement about the whole thing. Obviously, I realise that the resulting predicate won't be computable (because it is making a statement about infinite streams), so it might have a \forall in there somewhere, but that's fine - I want to use it for developing a theory, not generating code.
How can I define such a predicate? (And how could I prove the obvious associated introduction and elimination lemmas that make it useful?)
Thanks!
I suppose the most idiomatic way to do this is to use a coinductive predicate. Intuitively, this is like a normal inductive predicate except that you also allow ‘infinite derivation trees’:
type_synonym 'a graph = "'a ⇒ 'a ⇒ bool"
codatatype 'a llist = LNil | LCons 'a "'a llist"
coinductive is_path :: "'a graph ⇒ 'a llist ⇒ bool" for g :: "'a graph" where
is_path_LNil:
"is_path g LNil"
| is_path_singleton:
"is_path g (LCons x LNil)"
| is_path_LCons:
"g x y ⟹ is_path g (LCons y path) ⟹ is_path g (LCons x (LCons y path))"
This gives you introduction rules is_path.intros and an elimination rule is_path.cases.
When you want to show that an inductive predicate holds, you just use its introduction rules; when you want to show that an inductive predicate implies something else, you use induction with its induction rule.
With coinductive predicates, it is typically the other way round: When you want to show that a coinductive predicate implies something else, you just use its elimination rules. When you want to show that a coinductive predicate holds, you have to use coinduction.

Using the ordering locale with partial maps

The following code doesn't typecheck:
type_synonym env = "char list ⇀ val"
interpretation map: order "op ⊆⇩m :: (env ⇒ env ⇒ bool)" "(λa b. a ≠ b ∧ a ⊆⇩m b)"
by unfold_locales (auto intro: map_le_trans simp: map_le_antisym)
lemma
assumes "mono (f :: env ⇒ env)"
shows "True"
by simp
Isabelle complains with the following error at the lemma:
Type unification failed: No type arity option :: order
Type error in application: incompatible operand type
Operator: mono :: (??'a ⇒ ??'b) ⇒ bool
Operand: f :: (char list ⇒ val option) ⇒ char list ⇒ val option
Why so? Did I miss something to use the interpretation? I suspect I need something like a newtype wrapper here...
When you interpret a locale like order which corresponds to a type class, you only get the theorems proved inside the context of the locale. However, the constant mono is only defined on the type class. The reason is that mono's type contains two type variables, whereas only one is available inside locales from type classes. You can notice this because there is no map.mono stemming from your interpretation.
If you instantiate the type class order for the option type with None being less than Some x, then you can use mono for maps, because the function space instantiates order with the pointwise order. However, the ordering <= on maps will only be semantically equivalent to ⊆⇩m, not syntactically, so none of the existing theorems about ⊆⇩m will work for <= and vice versa. Moreover, your theories will be incompatible with other people's that instantiate order for option differently.
Therefore, I recommend to go without type classes. The predicate monotone explicitly takes the order to be used. This is a bit more writing, but in the end, you are more flexible than with type classes. For example, you can write monotone (op ⊆⇩m) (op ⊆⇩m) f to express that f is a monotone transformation of environments.

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

Why is my definition of a function that chooses an element from a finite set inconsistent?

I would like to reason about functions that choose one element from a finite set.
I tried to define a predicate that tells me whether some given function is such a “chooser” function:
definition chooser :: "('a set ⇒ 'a) ⇒ bool"
where "chooser f ⟷ (∀ A . finite A ⟶ f A ∈ A)"
Actually those finite sets from which I'd like to choose elements are of a concrete type, but putting a concrete type in 'a's place causes the same trouble.
I have also tried to omit finite A, but the sets I'm dealing with are finite, and I don't even want to think about the axiom of choice here.
Now this definition seems to be inconsistent:
lemma assumes "chooser f" shows "False" using assms chooser_def by force
How can I define chooser in a reasonable way? I would like to use it as follows:
assume "finite A"
moreover assume "chooser f"
moreover assume "choice = f A"
ultimately have "choice ∈ A" by ???
Most of the time it merely matters that a member of the set is chosen, not how it is chosen.
Background: I'd like to formalise tie-breakers in auctions (section 4 of this paper). Suppose there are two highest bids for the item being auctioned, we need to arbitrarily choose the one bidder who should win the auction.
Here is, BTW, a really minimal example (which is a bit harder to understand):
lemma "(∀ A . finite A ⟶ f A ∈ A) ⟹ False" by force
I merely provide the details based on Brian's comment that a choice function is defined only for a collection of non-empty sets.
From the Wikipedia entry on Choice_function:
A choice function (selector, selection) is a mathematical function f that is defined on some collection X of nonempty sets and assigns to each set S in that collection some element f(S) of S.
By now, you probably already have what you need from Brian's coment, but I do this anyway. The definition of chooser only needs the requirement that the set isn't empty:
definition chooser :: "('a set => 'a) => bool" where
"chooser f <-> (!A. A ~= {} --> f A ∈ A)"
theorem "(finite A & A ~= {} & chooser f) ==> (f A ∈ A)"
by(metis chooser_def)
theorem "(A ~= {} & chooser f) ==> (f A ∈ A)"
by(metis chooser_def)
You said that you don't want to use the Axiom of Choice, but a standard choice function demonstrates a good template to follow, not that you need it.
definition choice :: "'a set => 'a" where
"choice T = (SOME x. x ∈ T)"
theorem "T ~= {} ==> choice T ∈ T"
by(unfold choice_def, metis ex_in_conv someI
--GC

Resources