I am trying to define the Ackermann-Peters function in Coq, and I\'m getting an error message that I don\'t understand. As you can see, I\'m packaging the arguments a,
I just tried your function with Coq 8.4, and the error is slightly different:
Error: Nested recursive function are not allowed with Function
I guess the inner call to ack is the problem, but I don't know why.
Hope this helps a bit, V.
PS: The usual way I define Ack is what wires wrote, with an inner fixpoint.
It seems like Function
can't solve this problem. However, its cousin Program Fixpoint
can.
Let's define some lemmas treating well-foundedness first:
Require Import Coq.Program.Wf.
Require Import Coq.Arith.Arith.
Definition lexicographic_ordering (ab1 ab2 : nat * nat) : Prop :=
match ab1, ab2 with
| (a1, b1), (a2, b2) =>
(a1 < a2) \/ ((a1 = a2) /\ (b1 < b2))
end.
(* this is defined in stdlib, but unfortunately it is opaque *)
Lemma lt_wf_ind :
forall n (P:nat -> Prop), (forall n, (forall m, m < n -> P m) -> P n) -> P n.
Proof. intro p; intros; elim (lt_wf p); auto with arith. Defined.
(* this is defined in stdlib, but unfortunately it is opaque too *)
Lemma lt_wf_double_ind :
forall P:nat -> nat -> Prop,
(forall n m,
(forall p (q:nat), p < n -> P p q) ->
(forall p, p < m -> P n p) -> P n m) -> forall n m, P n m.
Proof.
intros P Hrec p. pattern p. apply lt_wf_ind.
intros n H q. pattern q. apply lt_wf_ind. auto.
Defined.
Lemma lexicographic_ordering_wf : well_founded lexicographic_ordering.
Proof.
intros (a, b); pattern a, b; apply lt_wf_double_ind.
intros m n H1 H2.
constructor; intros (m', n') [G | [-> G]].
- now apply H1.
- now apply H2.
Defined.
Now we can define the Ackermann-Péter function:
Program Fixpoint ack (ab : nat * nat) {wf lexicographic_ordering ab} : nat :=
match ab with
| (0, b) => b + 1
| (S a, 0) => ack (a, 1)
| (S a, S b) => ack (a, ack (S a, b))
end.
Next Obligation.
inversion Heq_ab; subst. left; auto. Defined.
Next Obligation.
apply lexicographic_ordering_wf. Defined.
Some simple tests demonstrating that we can compute with ack
:
Example test1 : ack (1, 2) = 4 := eq_refl.
Example test2 : ack (3, 4) = 125 := eq_refl. (* this may take several seconds *)
Using the Equations plugin by M. Sozeau and C. Mangin it is possible to define the function this way:
From Equations Require Import Equations Subterm.
Equations ack (p : nat * nat) : nat :=
ack p by rec p (lexprod _ _ lt lt) :=
ack (pair 0 n) := n + 1;
ack (pair (S m) 0) := ack (m, 1);
ack (pair (S m) (S n)) := ack (m, ack (S m, n)).
Unfortunately, it's not possible to use the ( , )
notation for pairs due to issue #81. The code is taken from Equation's test suite: ack.v.
You get this error because you are referencing the ack
function while you are defining it. Self reference is only allowed in Fixpoint
s (ie. recursive functions) but the problem is, as you probably know, that the Ackermann function is not a primitive recursive function.
See Coq'Art section 4.3.2.2 for some more information on this.
So one alternative way to define it is by inlining a second recursive function that is structurally recursive for the second argument; so something like
Fixpoint ack (n m : nat) : nat :=
match n with
| O => S m
| S p => let fix ackn (m : nat) :=
match m with
| O => ack p 1
| S q => ack p (ackn q)
end
in ackn m
end.