I am having trouble proving termination of the following function:
Fixpoint norm_union u v : regex :=
match u, v with
| Empty , v => v
| u , Empty => u
| Union u v, w => norm_union u (norm_union v w)
| u , Union v w => if eq_regex_dec u v
then Union v w
else if le_regex u v
then Union u (Union v w)
else Union v (norm_union u w)
| u , v => if eq_regex_dec u v
then u
else if le_regex u v
then Union u v
else Union v u
end.
where regex is the type of regular expressions and le_regex implements a total ordering on regular expressions. The source is page five of this document. The function occurs as part of a normalization function for regular expressions (formalized in Isabelle/HOL). The le_regex function is adapted from the same paper. I am using ascii to avoid parameterizing regex by a decidable total ordering (and want to extract the program).
Inductive regex : Set :=
| Empty : regex
| Epsilon : regex
| Symbol : ascii -> regex
| Union : regex -> regex -> regex
| Concat : regex -> regex -> regex
| Star : regex -> regex.
Lemma eq_regex_dec : forall u v : regex, {u = v} + {u <> v}.
Proof. decide equality; apply ascii_dec. Defined.
Fixpoint le_regex u v : bool :=
match u, v with
| Empty , _ => true
| _ , Empty => false
| Epsilon , _ => true
| _ , Epsilon => false
| Symbol a , Symbol b => nat_of_ascii a <=? nat_of_ascii b
| Symbol _ , _ => true
| _ , Symbol _ => false
| Star u , Star v => le_regex u v
| Star u , _ => true
| _ , Star v => false
| Union u1 u2 , Union v1 v2 => if eq_regex_dec u1 v1
then le_regex u2 v2
else le_regex u1 v1
| Union _ _ , _ => true
| _ , Union _ _ => false
| Concat u1 u2, Concat v1 v2 => if eq_regex_dec u1 v1
then le_regex u2 v2
else le_regex u1 v1
end.
I think the correct approach is to define a decreasing measure and use Program Fixpoint to prove termination. However, I'm having trouble coming up with the correct measure (attempts based on the number of operators have been unsuccessful). I have tried factoring the work into separate functions, but ran into similar problems. Any help would be appreciated, or hints pointing in the right direction.
Your code is more complex than what is usually handled with a measure function, because you have a nested recursive call in the following line:
Union u v, w => norm_union u (norm_union v w) (* line 5 *)
I suggest that you should not return a value in type regex, but in type {r : regex | size r < combined_size u v} for suitable notions of size and combined_size.
After several hours of study on your problem, it also turns out that your recursion relies on lexical ordering of the arguments. norm_union v w may well return Union v w, so you need that the argument pair (u, Union v w) is smaller than (Union u v, w).
So if you really want to use a measure, you need the weight of the left-hand side to be larger than the weight of the right-hand side, and you need the measure of a component of a Union to be less than the measure of the whole.
Because of the lexical ordering nature, I chose to not use a measure but a well-founded order. Also, I don't know Program Fixpoint well enough, so I developed a solution to your problem using another tool. The solution I came up with can be seen here on github. At least this shows all the decrease conditions that need to be proved.
After an extra day of work, I now have a more complete answer to this question. It is still visible at this link. This solution deserves a few comments.
First, I am using a function constructor called Fix (the long name is Coq.Init.Wf.Fix. This is a higher order function that can be used to define functions by well-founded recursion. I need a well founded order for this, this order is called order. Well founded orders were studied intensively in the early 2000s and they are still at the foundation of the Program Fixpoint command.
Second, the code you wrote performs case analyses on two values of type regex simultaneously, so this leads to 36 cases (a bit less, because there is no case analysis on the second argument when the first one is Empty). You don't see the 36 cases in your code, because several constructors are covered by the same rule where the pattern is just a variable. To avoid this multiplication of cases, I devised a specific inductive type for
the case analyses. I called this specific type arT. Then I define a function ar that maps any element of type regex to the corresponding element of arT. The type arT has three constructors instead of six, so pattern maching expressions will contain much less code and proofs will be less verbose.
Then I proceeded to define norm_union using Fix. As usual in Coq (and in most theorem provers, including Isabelle), the language of
recursive definitions ensures that recursive functions always terminate. In this case, this is done by imposing that recursive calls only happen on arguments that are smaller than the function's input. In this case, this is done by describing the body of the recursive function by a function that takes as first argument the initial input and as second argument the function that will be used to represent the recursive calls. The name of this function is norm_union_F and its type is as follows:
forall p : regex * regex,
forall g : (forall p', order p' p ->
{r : regex | size_regex r <= size_2regex p'}),
{r : regex | size_regex r <= size_2regex p}
In this type description, the name of the function used to represent recursive calls is g and we see that the type of g imposes that it can only be used on pairs of regex terms that are smaller than the initial argument p for the order named order. In this type description, we also see I chose to express that the returned type of the recursive calls is not regex but {r : regex | size_regex r <= size_2regex p'}. This is because we have to handle nested recursion, where outputs of recursive calls will be used as inputs of other recursive calls. This is the main trick of this answer.
Then we have the body of the norm_union_F function:
Definition norm_union_F : forall p : regex * regex,
forall g : (forall p', order p' p ->
{r : regex | size_regex r <= size_2regex p'}),
{r : regex | size_regex r <= size_2regex p} :=
fun p norm_union =>
match ar (fst p) with
arE _ eq1 => exist _ (snd p) (th1 p)
| arU _ u v eq1 =>
match ar (snd p) with
arE _ eq2 => exist _ (Union u v) (th2' _ _ _ eq1)
| _ => exist _ (proj1_sig
(norm_union (u,
proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
_)))
(th5' _ _ _ eq1
(proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(proj1_sig
(norm_union
(u, proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p) (th3' _ _ _ eq1))))))
(proj2_sig
(norm_union
(u, proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))))))
end
| arO _ d1 d2 =>
match ar (snd p) with
arE _ eq2 => exist _ (fst p) (th11' _)
| arU _ v w eq2 =>
if eq_regex_dec (fst p) v then
exist _ (Union v w) (th7' _ _ _ eq2)
else if le_regex (fst p) v then
exist _ (Union (fst p) (Union v w)) (th8' _ _ _ eq2)
else exist _ (Union v (proj1_sig (norm_union (fst p, w)
(th9' _ _ _ eq2))))
(th10' _ _ _ eq2
(proj1_sig (norm_union (fst p, w)
(th9' _ _ _ eq2)))
(proj2_sig (norm_union (fst p, w)
(th9' _ _ _ eq2))))
| arO _ d1 d2 =>
if eq_regex_dec (fst p) (snd p) then
exist _ (fst p) (th11' _)
else if le_regex (fst p) (snd p) then
exist _ (Union (fst p) (snd p)) (th12' _)
else exist _ (Union (snd p) (fst p)) (th13' _)
end
end.
In this code, all output values are within an exist _ context: not only do we produce the output value, but we also show that the size of this value is smaller than the combined size of the input pair of values. More over, all recursive calls are within a proj1_sig context, so that we forget the size information at the moment of constructing the output value. But also, all recursive calls, here represented by calls to the function named norm_union also have a proof that the input to the recursive call is indeed smaller than the initial input. All the proofs are in the complete development.
It would probably be possible to use tactics like refine to define norm_union_F, you are invited to explore.
Then we define the truly recursive function norm_union_1
Definition norm_union_1 : forall p : regex*regex,
{x | size_regex x <= size_2regex p} :=
Fix well_founded_order (fun p => {x | size_regex x <= size_2regex p})
norm_union_F.
Note that the output of norm_union_1 has type {x | size_regex x <= size_2regex p}. This is not the type you asked for. So we define a new function, which is really the one you want, simply by forgetting the logical information that the output has a size smaller than the input.
Definition norm_union u v : regex := proj1_sig (norm_union_1 (u, v)).
You might still doubt that this is the right function, the one you asked for. To convince ourselves, we are going to prove a lemma that expresses exactly what you would have said in a definition.
We first prove the corresponding lemma for norm_union_1. This relies on a theorem associated to the Fix function, name Fix_eq. The proof that needs to be done is fairly routine (it always is, it could be done automatically, but I never came around to developing the automatic tool for that).
Then we finish with most interesting lemma, the one for norm_union. Here is the statement:
Lemma norm_union_eqn u v :
norm_union u v =
match u, v with
| Empty , v => v
| u , Empty => u
| Union u v, w => norm_union u (norm_union v w)
| u , Union v w => if eq_regex_dec u v
then Union v w
else if le_regex u v
then Union u (Union v w)
else Union v (norm_union u w)
| u , v => if eq_regex_dec u v
then u
else if le_regex u v
then Union u v
else Union v u
end.
Please note that the right-hand-side of this equation is exactly the code that you gave in your initial question (I simply copy-pasted it). The proof of this final theorem is also fairly systematic.
Now, I made the effort of following exactly your request, but after the fact I discovered that there is a simple implementation of the same functionality, using three recursive functions. The first one flattens binary trees of Union to make then look like list, and the other two sort these union with respect to the order le_regex while removing duplicates as soon as they are uncovered. Such an implementation would workaround the need for nested recursion.
If you still want to stick to nested recursion and need to refer to the technique described here, it was first published in a paper by Balaa and Bertot at TPHOLs2000. That paper is difficult to read because it was written at a time when Coq was using a different syntax.
Related
I have made the following inductive data type and recursive functions.
Require Coq.Lists.List.
Inductive PairTree : Type :=
| PTLeaf : PairTree
| PTBranch : (nat*nat) -> PairTree -> PairTree -> PairTree.
Fixpoint smush (r m : list nat) : list nat :=
match m with
| nil => r
| h :: t => smush (h :: r) t
end.
I've defined the following function using these, with an auxiliary "gas" parameter so that Coq's termination rules are easily satisfied.
Fixpoint fueled_prefix_part (gas : nat) (r m : list nat) : PairTree :=
match gas with
| 0 => PTLeaf
| S gas' =>
match r with
| nil => PTLeaf
| f :: fs =>
match fs with
| nil => PTLeaf
| f' :: fs' =>
PTBranch (f, f')
(fueled_prefix_part gas' (smush fs' m) nil)
(fueled_prefix_part gas' (f :: fs') (f' :: m))
end
end
end.
However, I wanted to extract actual code from this and run it. Thus, instead of structural recursion on gas, I could get nicer extracted code if instead I could use the following code, since then I could use some other nat-like data type with < (e.g. Haskell Integers).
Require Import Recdef.
Require Export Coq.omega.Omega.
Require Export Coq.Lists.List.
(* Insert definitions of PairTree and smush here. *)
Function fpp' (gas: nat) (r m: list nat) {measure (fun x => x) gas}: PairTree :=
match 0 <? gas with
| false => PTLeaf
| true =>
match r with
| nil => PTLeaf
| f :: fs =>
match fs with
| nil => PTLeaf
| f' :: fs' =>
let gas' := gas - 1 in
PTBranch (f, f')
(fpp' gas' (smush fs' m) nil)
(fpp' gas' (f :: fs') (f' :: m))
end
end
end.
Of course we can't end there; we have to prove that this terminates. The following would appear to prove the termination...
Proof.
intro gas. destruct gas; intros _ _ teq _ _ _ _ _ _.
inversion teq.
omega.
intro gas. destruct gas; intros _ _ teq _ _ _ _ _ _.
inversion teq.
omega.
At this point in ProofGeneral I see no goals in the goal screen, and in the response screen, I see the message
No more subgoals.
(dependent evars:)
So I type in
Defined.
and get the error message
Toplevel input, characters 0-8:
Error: Attempt to save a proof with shelved goals (in proof fpp'_terminate)
What on earth is going on? If I type
Unshelve.
Defined.
I get the same error.
I am trying to use List.map in recursive definition, mapping over a list using currently defined recursive function as an argument. Is it possible at all? I can define my own recursive fixpoint definition instead of using map but I am interested in using map here.
Require Import Coq.Lists.List.
Import ListNotations.
Inductive D: nat -> Type := | D0 (x:nat): D x.
Inductive T: nat -> nat -> Type :=
| T0 {i o} (foo:nat): T i o
| T1 {i o} (foo bar:nat) : T i o -> T i o.
Fixpoint E {i o: nat} (t:T i o) (x:nat) (d:D i): option (D o)
:=
(match t in #T i o
return D i -> option (D o)
with
| T0 _ _ foo => fun d0 => None
| T1 _ _ foo bar t' =>
fun d0 =>
let l := List.map (fun n => E t' x d0) [ 1 ; 2 ; 3 ] in
let default := Some (D0 o) in
List.hd default l
end) d.
The example above is artificial, but demonstrates the problem. The error message:
The term "l" has type "list (option (D n0))"
while it is expected to have type "list (option (D o))".
You just need to bind the names on the T1 pattern:
Require Import Coq.Lists.List.
Import ListNotations.
Inductive D: nat -> Type := | D0 (x:nat): D x.
Inductive T: nat -> nat -> Type :=
| T0 {i o} (foo:nat): T i o
| T1 {i o} (foo bar:nat) : T i o -> T i o.
Fixpoint E {i o: nat} (t:T i o) (x:nat) (d:D i): option (D o)
:=
(match t in #T i o
return D i -> option (D o)
with
| T0 _ _ foo => fun d0 => None
(* \/ change here *)
| T1 i o foo bar t' =>
fun d0 =>
let l := List.map (fun n => E t' x d0) [ 1 ; 2 ; 3 ] in
let default := Some (D0 o) in
List.hd default l
end) d.
The problem is that omitting the binders means that the o used on the T1 branch refers to the "outer" variable of the same name, whereas you want it to refer to the one given by T1.
Implementing vector addition in some of the dependently typed languages (such as Idris) is fairly straightforward. As per the example on Wikipedia:
import Data.Vect
%default total
pairAdd : Num a => Vect n a -> Vect n a -> Vect n a
pairAdd Nil Nil = Nil
pairAdd (x :: xs) (y :: ys) = x + y :: pairAdd xs ys
(Note how Idris' totality checker automatically infers that addition of Nil and non-Nil vectors is a logical impossibility.)
I am trying to implement the equivalent functionality in Coq, using a custom vector implementation, albeit very similar to the one provided in the official Coq libraries:
Set Implicit Arguments.
Inductive vector (X : Type) : nat -> Type :=
| vnul : vector X 0
| vcons {n : nat} (h : X) (v : vector X n) : vector X (S n).
Arguments vnul [X].
Fixpoint vpadd {n : nat} (v1 v2 : vector nat n) : vector nat n :=
match v1 with
| vnul => vnul
| vcons _ x1 v1' =>
match v2 with
| vnul => False_rect _ _
| vcons _ x2 v2' => vcons (x1 + x2) (vpadd v1' v2')
end
end.
When Coq attempts to check vpadd, it yields the following error:
Error:
In environment
vpadd : forall n : nat, vector nat n -> vector nat n -> vector nat n
[... other types]
n0 : nat
v1' : vector nat n0
n1 : nat
v2' : vector nat n1
The term "v2'" has type "vector nat n1" while it is expected to have type "vector nat n0".
Note that, I use False_rect to specify the impossible case, otherwise the totality check wouldn't pass. However, for some reason the type checker doesn't manage to unify n0 with n1.
What am I doing wrong?
It's not possible to implement this function so easily in plain Coq: you need to rewrite your function using the convoy pattern. There was a similar question posted a while ago about this. The idea is that you need to make your match return a function in order to propagate the relation between the indices:
Set Implicit Arguments.
Inductive vector (X : Type) : nat -> Type :=
| vnul : vector X 0
| vcons {n : nat} (h : X) (v : vector X n) : vector X (S n).
Arguments vnul [X].
Definition vhd (X : Type) n (v : vector X (S n)) : X :=
match v with
| vcons _ h _ => h
end.
Definition vtl (X : Type) n (v : vector X (S n)) : vector X n :=
match v with
| vcons _ _ tl => tl
end.
Fixpoint vpadd {n : nat} (v1 v2 : vector nat n) : vector nat n :=
match v1 in vector _ n return vector nat n -> vector nat n with
| vnul => fun _ => vnul
| vcons _ x1 v1' => fun v2 => vcons (x1 + vhd v2) (vpadd v1' (vtl v2))
end v2.
I want to prove that computing the cumulative sum between a and b terminates.
I use an Acc lt x term to show that the recursion decreases, like this
Require Import Omega.
Lemma L1 : forall a b, a<b -> (b-(1+a)) < (b-a).
intros; omega. Qed.
Lemma term_lemma: forall a b, Acc lt (b-a) -> Acc lt (b-(1+a)).
intros; inversion H; clear H; constructor; intros; apply H0; omega.
Defined.
Fixpoint cumsum a b (H: Acc lt (b-a)) {struct H} : nat.
refine (
match lt_dec a b with
| left a_lt_b => a + cumsum (1+a) b _
| right a_ge_b => if beq_nat a b then a else 0
end
).
apply (term_lemma _ _ H).
Qed.
It clears all subgoals but it won't typecheck at the Qed statement. Coq complains:
Recursive definition of cumsum is ill-formed
Recursive call to cumsum has principal argument equal to
"term_lemma a b H" instead of a subterm of "H".
I guess I should somehow use L1 to show that the argument in the H term in the recursive call is actually smaller, but how do I do that?
Because you invert H before building something similar-ish back again by using constructor ; apply H0, you get a term_lemma with a pattern matching that's equivalent to what you'd want but confuses Coq's termination checker (You can inspect a term by using Print NAME.).
You don't need to do all of this inversion business if you remember that you already know that a < b thanks to your case analysis on lt_dec a b. By letting your lemma take an extra argument, you can now use the strict subterm of the Accessibility predicate to get your witness:
Require Import Omega.
Lemma term_lemma: forall a b, a < b -> Acc lt (b-a) -> Acc lt (b-(1+a)).
intros a b altb [H]; apply H; omega.
Defined.
Fixpoint cumsum a b (H: Acc lt (b-a)) {struct H} : nat.
refine (
match lt_dec a b with
| left a_lt_b => a + cumsum (1+a) b _
| right a_ge_b => if beq_nat a b then a else 0
end
).
apply (term_lemma _ _ a_lt_b H).
Defined.
In A Tutorial on[Co-]Inductive Types in Coq on p. 47, a recursive function is defined, where each recursive step uses a well-formedness proposition to show that the recursion terminates.
A function that is called with x makes a recursive call with x-y where y<>0, so it should terminate.
I am not able to enter it into Coq without getting an error. Coq is complaining that the recursion argument is not smaller in the call, while the tutorial claims that it is so.
What am I missing?
I rewrote the code slightly to make it shorter, but I also tried the verbatim definitions in the paper.
First we show that x-y is accessible from x.
Require Import Omega.
Definition minus_decrease:
forall x y, Acc lt x -> x<>0 -> y<>0 -> Acc lt (x-y).
intros x y H Hx Hy.
case H; intro Ha; apply Ha.
omega.
Qed.
Next, when trying to define the function, like this
Definition div_aux :=
fix div_aux (x y:nat) (H:Acc lt x) {struct H}: nat :=
match eq_nat_dec x 0 with
|left _ => 0
|right _ =>
match eq_nat_dec y 0 with
|left _ => 0
|right v => S (div_aux (x-y) y (minus_decrease x y H _ v))
end
end.
then Coq refuses, saying
Recursive call to div_aux has principal argument equal to
"minus_decrease x y H ?156 v" instead of a subterm of "H".
Notice how div_aux x ... calls itself recursively with div_aux (x-y) ..., and (minus_decrease ...) returns a term of type Acc lt (x-y)
How do I use Acc to show that this function actually terminates?
The error seems to be that I ended the definition with Qed. instead of Defined. The following works.
Require Import Omega.
Definition minus_decrease: forall x y, Acc lt x -> x<>0 -> y<>0 -> Acc lt (x-y).
intros x y H Hx Hy.
case H; intro Ha; apply Ha.
omega.
Defined.
Fixpoint div_aux (x y:nat) (H:Acc lt x) {struct H}: nat.
Proof.
refine (if eq_nat_dec x 0
then 0
else if eq_nat_dec y 0
then y
else S (div_aux (x-y) y _)).
apply (minus_decrease _ _ H _H _H0).
Qed.