Zulip Chat Archive
Stream: new members
Topic: Termination proof of mutual rec functions
Rikiya Kashiwagi (Nov 15 2024 at 01:13):
This is a rather general question, but I have no idea how to show termination for these mutually recursive functions.
mutual
def loop1 : List α → List α := loop2
def loop2 : List α → List α
| [] => []
| a :: as => loop1 as
end
This code fails to show termination because the argument of loop2 in the definition of loop1 does not decrease, but it's obvious that loop1 terminates.
I am now struggled with a more complicated version of this kind of mutual rec functions, so merging the defnitions of the two is not an intended solution.
nrs (Nov 15 2024 at 01:29):
replace loopx : List a -> List a
with loopx (l : List a) : List a
in both cases
nrs (Nov 15 2024 at 01:33):
the termination check does not have anything it can look for otherwise
Rikiya Kashiwagi (Nov 15 2024 at 01:43):
Thanks. then how to specify termination_by.
def loop1 (l : List α) : List α := loop2 l
termination_by l
def loop2 (l : List α) : List α
| [] => []
| a :: as => loop1 as
termination_by l
termination_by is not needed in this example, but it throws an error at loop2 l
.
nrs (Nov 15 2024 at 01:47):
you must use l
as follows:
mutual
def loop1 (l : List α) : List α := loop2 l
def loop2 (l : List α) : List α :=
match l with
| [] => []
| a :: as => loop1 as
end
Rikiya Kashiwagi (Nov 15 2024 at 01:49):
Yes. sorry for confusion, but my question is still the same
nrs (Nov 15 2024 at 02:09):
hm this is not as obvious as i thought do you have an example of the code you need to prove this for? are you sure you cannot just use structural recursion? I am not sure how to prove wellfounded recursion here
nrs (Nov 15 2024 at 03:18):
duplicate
nrs (Nov 15 2024 at 03:19):
Rikiya Kashiwagi
here you go!
mutual
def loop1 (l :List α) : List α := loop2 l
termination_by (sizeOf l, 1)
def loop2 (l : List α) : List α :=
match l with
| [] => []
| a :: as => loop1 as
termination_by (sizeOf l, 0)
end
I used termination_by?
to find the argument. the second argument in the termination_by
statement specifies which function terminates first. passing this sort of pair to a termination_by
statement only exists in mutually recursive definitions (from what I can see so far, only in where
and mutual
blocks)
nrs (Nov 15 2024 at 04:00):
here is an example from source I found instructive
mutual
def f : Nat → Bool → Nat
| n, true => 2 * f n false
| 0, false => 1
| n, false => n + g n
termination_by n b => (n, if b then 2 else 1)
def g (n : Nat) : Nat :=
if h : n ≠ 0 then
f (n-1) true
else
n
termination_by (n, 0)
end
nrs (Nov 15 2024 at 04:01):
but you can ignore these details and just omit the termination_by
statement in your case, you only need to worry about these if termination is not automatically inferred
Rikiya Kashiwagi (Nov 15 2024 at 06:57):
Indexing iterations makes sense!
Thank you for considering the meaningful example.
I am writing a unification algorithm whose termination proof is not obvious. To avoid duplication, I need to implement unification algorithm as mutually recursive functions.
Last updated: May 02 2025 at 03:31 UTC