Widening the domain of a partial function - isabelle

Apologies for the unprecise title.
I defined a function computing fixed-points like
function (domintros) "fix" :: "(env ⇒ env) ⇒ env ⇒ env"
where
"fix f m n = (if f m n = m n then m n else fix f (f m) n)"
by pat_completeness blast
where type_synonym env = char list ⇀ val is just a usual (explicit) partial function (map).
I managed to prove termination under certain conditions, but now I want to prove the following lemma, which seems to hold trivially:
lemma fix_dom_iter [intro]:
assumes "fix_dom (f, m, n)"
shows "fix_dom (f, f m, n)"
oops
But only seemingly, since I can't find out how to prove it. There seems to be no fix.p* theorem that does what I want.
The hard case is obviously when ~(f m n = m n).
Any pointers wrt. how to prove the lemma? Is it even provable?
Edit:
I think that fix.domintros should rather be an equivalence than an implication. That way, I would be able to prove what I want and it should in fact be sound. Question is, if this is sound for any kind of partial function.

Unfortunately your lemma does not hold. Consider the following variant of your input where I changed the environment to just functions from bool to bool.
type_synonym env = "bool ⇒ bool"
function (domintros) "fix" :: "(env ⇒ env) ⇒ env ⇒ env"
where
"fix f m n = (if f m n = m n then m n else fix f (f m) n)"
by pat_completeness blast
I also define a corresponding executable partial function.
partial_function (tailrec) "fix2" :: "(env ⇒ env) ⇒ env ⇒ env"
where
[code]: "fix2 f m n = (if f m n = m n then m n else fix2 f (f m) n)"
Now the following witness of f, m, and n show that your statement is not true.
definition "f m = (if m True = m False then Not else Not o m)"
definition "m n = True"
definition "n = False"
fix_dom (f,m,n) indeed holds.
lemma "fix_dom (f,m,n)" unfolding f_def[abs_def] m_def[abs_def] n_def
by (auto intro: fix.domintros)
But, fix_dom (f,f m,n) does not hold as can be observed by a non-terminating computation.
value (code) "fix2 f (f m) n"

Related

Why do I get this exception on an induction rule for a lemma?

I am trying to prove the following lemma (which is the meaning formula for the addition of two Binary numerals).
It goes like this :
lemma (in th2) addMeaningF_2: "∀m. m ≤ n ⟹ (m = (len x + len y) ⟹ (evalBinNum_1 (addBinNum x y) = plus (evalBinNum_1 x) (evalBinNum_1 y)))"
I am trying to perform strong induction. When I apply(induction n rule: less_induct) on the lemma, it throws an error.
exception THM 0 raised (line 755 of "drule.ML"):
infer_instantiate_types: type ?'a of variable ?a
cannot be unified with type 'b of term n
(⋀x. (⋀y. y < x ⟹ ?P y) ⟹ ?P x) ⟹ ?P ?a
Can anyone explain this?
Edit:
For more context
locale th2 = th1 +
fixes
plus :: "'a ⇒ 'a ⇒ 'a"
assumes
arith_1: "plus n zero = n"
and plus_suc: "plus n (suc m) = suc ( plus n m)"
len and evalBinNum_1 are both recursive functions
len gives us the length of a given binary numeral, while evalBinNum_1 evaluates binary numerals.
fun (in th2) evalBinNum_1 :: "BinNum ⇒ 'a"
where
"evalBinNum_1 Zero = zero"|
"evalBinNum_1 One = suc(zero)"|
"evalBinNum_1 (JoinZero x) = plus (evalBinNum_1 x) (evalBinNum_1 x)"|
"evalBinNum_1 (JoinOne x) = plus (plus (evalBinNum_1 x) (evalBinNum_1 x)) (suc zero)"
The problem is that Isabelle cannot infer the type of n (or the bound occurrence of m) when trying to use the induction rule less_induct. You might want to add a type annotation such as (n::nat) in your lemma. For the sake of generality, you might want to state that the type of n is an instance of the class wellorder, that is, (n::'a::wellorder). On another subject, I think there is a logical issue with your lemma statement: I guess you actually mean ∀m. m ≤ (n::nat) ⟶ ... ⟶ ... or, equivalently, ⋀m. m ≤ (n::nat) ⟹ ... ⟹ .... Finally, it would be good to know the context of your problem (e.g., there seems to be a locale th2 involved) for a more precise answer.

Simplifying if-then-else in summations or products

While doing some basic algebra, I frequently arrive at a subgoal of the following type (sometimes with a finite sum, sometimes with a finite product).
lemma foo:
fixes N :: nat
fixes a :: "nat ⇒ nat"
shows "(a 0) = (∑x = 0..N. (if x = 0 then 1 else 0) * (a x))"
This seems pretty obvious to me, but neither auto nor auto cong: sum.cong split: if_splits can handle this. What's more, sledgehammer also surrenders when called on this lemma. How can one efficiently work with finite sums and products containing if-then-else in general, and how to approach this case in particular?
My favourite way to do these things (because it is very general) is to use the rules sum.mono_neutral_left and sum.mono_neutral_cong_left and the corresponding right versions (and analogously for products). The rule sum.mono_neutral_right lets you drop arbitrarily many summands if they are all zero:
finite T ⟹ S ⊆ T ⟹ ∀i∈T - S. g i = 0
⟹ sum g T = sum g S
The cong rule additionally allows you to modify the summation function on the now smaller set:
finite T ⟹ S ⊆ T ⟹ ∀i∈T - S. g i = 0 ⟹ (⋀x. x ∈ S ⟹ g x = h x)
⟹ sum g T = sum h S
With those, it looks like this:
lemma foo:
fixes N :: nat and a :: "nat ⇒ nat"
shows "a 0 = (∑x = 0..N. (if x = 0 then 1 else 0) * a x)"
proof -
have "(∑x = 0..N. (if x = 0 then 1 else 0) * a x) = (∑x ∈ {0}. a x)"
by (intro sum.mono_neutral_cong_right) auto
also have "… = a 0"
by simp
finally show ?thesis ..
qed
Assuming the left-hand side could use an arbitrary value between 0 and N, what about adding a more general lemma
lemma bar:
fixes N :: nat
fixes a :: "nat ⇒ nat"
assumes
"M ≤ N"
shows "a M = (∑x = 0..N. (if x = M then 1 else 0) * (a x))"
using assms by (induction N) force+
and solving the original one with using bar by blast?

Getting a function from a forall exists fact

My aim is to get a function constant f from a fact of the form ∀ x . ∃ y . P x y so that ∀ x . P x (f x). Here is how I do it manually:
theory Choose
imports
Main
begin
lemma
fixes P :: "nat ⇒ nat ⇒ nat ⇒ nat ⇒ nat ⇒ bool"
shows True
proof -
(* Somehow obtained this fact *)
have I: "∀ n m :: nat . ∃ i j k . P n m i j k"
by sorry
(* Have to do the rest by hand *)
define F
where "F ≡ λ n m . SOME (i, j, k) . P n m i j k"
define i
where "i ≡ λ n m . fst (F n m)"
define j
where "j ≡ λ n m . fst (snd (F n m))"
define k
where "k ≡ λ n m . snd (snd (F n m))"
have "∀ n m . P n m (i n m) (j n m) (k n m)"
(* prove manually (luckily sledgehammer finds a proof)*)
(*...*)
qed
(* or alternatively: *)
lemma
fixes P :: "nat ⇒ nat ⇒ nat ⇒ nat ⇒ nat ⇒ bool"
shows True
proof -
(* Somehow obtained this fact *)
have I: "∀ n m :: nat . ∃ i j k . P n m i j k"
by sorry
obtain i j k where "∀ n m . P n m (i n m) (j n m) (k n m)"
(* sledgehammer gives up (due to problem being too higher order?) *)
(* prove by hand :-( *)
(*...*)
qed
How to do this more ergonomically? Does Isabelle have something like
Leans choose tactic (https://leanprover-community.github.io/mathlib_docs/tactics.html#choose) ?
(Isabelles specification command only works on the top level :-( ).
(Sorry if this has been asked already, I didn't really find a good buzzword to search for this issue)
I don't think there is anything that automates this use case. You can avoid fiddling around with SOME by using the choice rule directly; it allows you to turn an ‘∀∃’ into a ‘∃∀’. However, you still have to convert P from a curried property with 5 arguments into a tupled one with two arguments first, and then unwrap the result again. I don't see a way around this. This is how I would have done it:
let ?P' = "λ(n,m). λ(i,j,k). P n m i j k"
have I: "∀n m. ∃i j k. P n m i j k"
sorry
hence "∀nm. ∃ijk. ?P' nm ijk"
by blast
hence "∃f. ∀nm. ?P' nm (f nm)"
by (rule choice) (* "by metis" also works *)
then obtain f where f: "?P' (n, m) (f (n, m))" for n m
by auto
define i where "i = (λn m. case f (n, m) of (i, j, k) ⇒ i)"
define j where "j = (λn m. case f (n, m) of (i, j, k) ⇒ j)"
define k where "k = (λn m. case f (n, m) of (i, j, k) ⇒ k)"
have ijk: "P n m (i n m) (j n m) (k n m)" for n m
using f[of n m] by (auto simp: i_def j_def k_def split: prod.splits)
In principle, I am sure this could be automated. I don't think there is any reason why the specification command should only work on the top level and not in local contexts or even Isar proofs, other than that it is old and nobody ever bothered to do it. That said, it would of course mean quite a bit of implementation effort and I for one have encountered situations like this relatively rarely and the boilerplate for applying choice by hand as above is not that bad.
But it would certainly be nice to have automation for this!

Defining Primtive Recursion for multiplication in Isabelle

I am new to Isabelle and I am trying to define primitive recursive functions. I have tried out addition but I am having trouble with multiplication.
datatype nati = Zero | Suc nati
primrec add :: "nati ⇒ nati ⇒ nati" where
"add Zero n = n" |
"add (Suc m) n = Suc(add m n)"
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Suc(Zero) n = n" |
"mult (Suc m) n = add((mult m n) m)"
I get the following error for the above code
Type unification failed: Clash of types "_ ⇒ _" and "nati"
Type error in application: operator not of function type
Operator: mult m n :: nati
Operand: m :: nati
Any ideas?
The problem is your mult function: It should look like this:
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Zero n = Zero" |
"mult (Suc m) n = add (mult m n) m"
Function application in functional programming/Lambda calculus is the operation that binds strongest and it associates to the left: something like f x y means ‘f applied to x, and the result applied to y’ – or, equivalently due to Currying: the function f applied to the parameters x and y.
Therefore, something like mult Suc(Zero) n would be read as mult Suc Zero n, i.e. the function mult would have to be a function taking three parameters, namely Suc, Zero, and n. That gives you a type error. Similarly, add ((mult m n) m) does not work, since that is identical to add (mult m n m), which would mean that add is a function taking one parameter and mult is one taking three.
Lastly, if you fix all that, you will get another error saying you have a non-primitive pattern on the left-hand side of your mult function. You cannot pattern-match on something like Suc Zero since it is not a primitive pattern. You can do that if you use fun instead of primrec, but it is not what you want to do here: You want to instead handle the cases Zero and Suc (see my solution). In your definition, mult Zero n would even be undefined.

Free type variables in proof by induction

While trying to prove lemmas about functions in continuation-passing style by induction I have come across a problem with free type variables. In my induction hypothesis, the continuation is a schematic variable but its type involves a free type variable. As a result Isabelle is not able to unify the type variable with a concrete type when I try to apply the i.h. I have cooked up this minimal example:
fun add_k :: "nat ⇒ nat ⇒ (nat ⇒ 'a) ⇒ 'a" where
"add_k 0 m k = k m" |
"add_k (Suc n) m k = add_k n m (λn'. k (Suc n'))"
lemma add_k_cps: "∀k. add_k n m k = k (add_k n m id)"
proof(rule, induction n)
case 0 show ?case by simp
next
case (Suc n)
have "add_k (Suc n) m k = add_k n m (λn'. k (Suc n'))" by simp
also have "… = k (Suc (add_k n m id))"
using Suc[where k="(λn'. k (Suc n'))"] by metis
also have "… = k (add_k n m (λn'. Suc n'))"
using Suc[where k="(λn'. Suc n')"] sorry (* Type unification failed *)
also have "… = k (add_k (Suc n) m id)" by simp
finally show ?case .
qed
In the "sorry step", the explicit instantiation of the schematic variable ?k fails with
Type unification failed
Failed to meet type constraint:
Term: Suc :: nat ⇒ nat
Type: nat ⇒ 'a
since 'a is free and not schematic. Without the instantiation the simplifier fails anyway and I couldn't find other methods that would work.
Since I cannot quantify over types, I don't see any way how to make 'a schematic inside the proof. When a term variable becomes schematic locally inside a proof, why isn't this the case with variables in its type too? After the lemma has been proved, they become schematic at the theory level anyway. This seems quite limiting. Could an option to do this be implemented in the future or is there some inherent limitation? Alternatively, is there an approach to avoid this problem and still keeping the continuation schematically polymorphic in the proven lemma?
Free type variables become schematic in a theorem when the theorem is exported from the block in which the type variables have been fixed. In particular, you cannot quantify over type variables in a block and then instantiate the type variable within the block, as you are trying to do in your induction. Arbitrary quantification over types leads to inconsistencies in HOL, so there is little hope that this could be changed.
Fortunately, there is a way to prove your lemma in CPS style without type quantification. The problem is that your statement is not general enough, because it contains id. If you generalise it, then the proof works:
lemma add_k_cps: "add_k n m (k ∘ f) = k (add_k n m f)"
proof(induction n arbitrary: f)
case 0 show ?case by simp
next
case (Suc n)
have "add_k (Suc n) m (k ∘ f) = add_k n m (k ∘ (λn'. f (Suc n')))" by(simp add: o_def)
also have "… = k (add_k n m (λn'. f (Suc n')))"
using Suc.IH[where f="(λn'. f (Suc n'))"] by metis
also have "… = k (add_k (Suc n) m f)" by simp
finally show ?case .
qed
You get your original theorem back, if you choose f = id.
This is an inherent limitation how induction works in HOL. Induction is a rule in HOL, so it is not possible to generalize any types in the induction hypothesis.
A specialized solution for your problem is to first prove
lemma add_k_cps_nat: "add_k n m k = k (n + m)"
by (induction n arbitrary: m k) auto
and then prove add_k_cps.
A general approach is: prove instances for fixed types first, for which the induction works. In the example case is is an induction by nat. And then derive a proof generalized in the type itself.

Resources