Termination checking failed - agda

I was trying to train on this kata about longest common subsequences of a list which I slightly modified so that it worked with my versions of agda and the standard library (Agda 2.6.2, stdlib 1.7) which results in this code
{-# OPTIONS --safe #-}
module pg where
open import Data.List
open import Data.Nat
open import Data.Product
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Relation.Binary
data Subseq {n} { A : Set n } : List A → List A → Set where
subseq-nil : Subseq [] []
subseq-take : ∀ a xs ys → Subseq xs ys → Subseq (a ∷ xs) (a ∷ ys)
subseq-drop : ∀ a xs ys → Subseq xs ys → Subseq xs (a ∷ ys)
is-lcs : ∀ {n} {A : Set n} → List A → List A → List A → Set n
is-lcs zs xs ys =
(Subseq zs xs × Subseq zs ys) ×
(∀ ts → Subseq ts xs → Subseq ts ys → length ts ≤ length zs)
longest : ∀ {n} {A : Set n} → List A → List A → List A
longest s1 s2 with length s1 ≤? length s2
... | yes _ = s2
... | no _ = s1
lcs : ∀ {n} {A : Set n} → Decidable {A = A} _≡_ → List A → List A → List A
lcs _ [] _ = []
lcs _ _ [] = []
lcs dec (x ∷ xs) (y ∷ ys) with dec x y
... | yes _ = x ∷ lcs dec xs ys
... | no _ = longest (lcs dec (x ∷ xs) ys) (lcs dec xs (y ∷ ys))
Unfortunetaly, Agda fails to recognize that lcs is a terminating function, which I honestly don't understand : the recursive calls are made on structurally smaller arguments if I get it right ?
If anyone can explain me what the problem is here, it would help tremendously. Thanks in advance !

Try to avoid using with when dealing with termination. The termination checker is successful when your code is refactored as follows (note that I removed your first two definitions because they are irrelevant in your question, maybe you should edit it accordingly):
{-# OPTIONS --safe #-}
open import Data.List
open import Data.Nat
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Relation.Binary
open import Data.Bool using (if_then_else_)
module Term where
longest : ∀ {a} {A : Set a} → List A → List A → List A
longest s1 s2 with length s1 ≤? length s2
... | yes _ = s2
... | no _ = s1
lcs : ∀ {a} {A : Set a} → Decidable {A = A} _≡_ → List A → List A → List A
lcs _ [] _ = []
lcs _ _ [] = []
lcs dec (x ∷ xs) (y ∷ ys) = if does (dec x y) then
x ∷ lcs dec xs ys else
longest (lcs dec (x ∷ xs) ys) (lcs dec xs (y ∷ ys))
Apparently, this is a known limitation of the with abstraction combined with the termination checker as noted in the wiki:
https://agda.readthedocs.io/en/v2.6.2.1/language/with-abstraction.html#termination-checking
Here is a similar question:
Failing termination check with a with-abstraction

Related

With expression non evaluation

I am trying to define a CoList without the delay constructors. I am running into a problem where I use a with expression but agda doesn't refine the type of a subcase.
module Failing where
open import Data.Unit
open import Data.Empty
open import Data.Maybe
open import Data.Nat
open import Data.Vec hiding (head ; tail ; map ; take)
record CoList (A : Set) : Set where
coinductive
field
head : Maybe A
tail : maybe (λ _ → ⊤) ⊥ head -> CoList A
open CoList
nil : ∀ {A} -> CoList A
head nil = nothing
tail nil ()
cons : ∀ {A} -> A -> CoList A -> CoList A
head (cons x xs) = just x
tail (cons x xs) tt = xs
take : ∀ {A} -> CoList A -> (n : ℕ) -> Maybe (Vec A n)
take l zero = just []
take l (suc n) with head l
... | nothing = nothing
... | just x = map (λ xs → x ∷ xs) (take (tail l {!!}) n)
The type of that hole is maybe (λ _ → ⊤) ⊥ (head l) but because of the with expression I would expect the type to be ⊤. I expect this because I withed on the head l and in that case head l = just x. If I try to fill the whole with tt agda mode gives me the following error:
⊤ !=< (maybe (λ _ → ⊤) ⊥ (head l)) of type Set
when checking that the expression tt has type
(maybe (λ _ → ⊤) ⊥ (head l))
I answered the question below, so now I am curious is there a better way to encode this list without the delay constructor?
You can think of with t as replacing t by whatever you match against, in the types of both function arguments and the goal. However, head l does not appear in your goal type when you perform the with — a goal whose type involves head l only appears later, once you have partially constructed the solution. This is the reason why your initial attempt doesn't work.
The inspect idiom, as demonstrated in your answer, is indeed the usual solution for this sort of problem.
As for encodings of coinductive types with 'more than one constructor', there are two (closely related) approaches that I'm aware of:
A mutual inductive/coinductive type:
data CoList′ (A : Set) : Set
record CoList (A : Set) : Set
data CoList′ A where
[] : CoList′ A
_∷_ : A → CoList A → CoList′ A
record CoList A where
coinductive
field
unfold : CoList′ A
open CoList
repeat : ∀ {A} → A → CoList A
repeat x .unfold = x ∷ repeat x
take : ∀ {A} → ℕ → CoList A → List A
take zero _ = []
take (suc n) xs with unfold xs
... | [] = []
... | x ∷ xs′ = x ∷ take n xs′
Taking the cofixpoint explicitly:
data CoList′ (A : Set) (CoList : Set) : Set where
[] : CoList′ A CoList
_∷_ : A → CoList → CoList′ A CoList
record CoList (A : Set) : Set where
coinductive
field
unfold : CoList′ A (CoList A)
open CoList
repeat : ∀ {A} → A → CoList A
repeat x .unfold = x ∷ repeat x
take : ∀ {A} → ℕ → CoList A → List A
take zero _ = []
take (suc n) xs with unfold xs
... | [] = []
... | x ∷ xs′ = x ∷ take n xs′
One solution I found is to use the inspect idiom. Apparently with abstractions in agda don't propagate equalities. The inspect idiom makes the equality obvious.
data Uncons (A : Set) : Set where
Nil : Uncons A
Cons : A -> CoList A -> Uncons A
uncons : ∀ {A} -> CoList A -> Uncons A
uncons l with head l | inspect head l
uncons l | nothing | _ = Nil
uncons l | just x | [ p ] = Cons x (tail l (subst (maybe (λ _ -> ⊤) ⊥) (sym p) tt))
take : ∀ {A} -> CoList A -> (n : ℕ) -> Maybe (Vec A n)
take l zero = just []
take l (suc n) with uncons l
... | Nil = nothing
... | Cons x xs = map (λ rest → x ∷ rest) (take xs n)

How to inspect the value inside a product (Σ)?

I am trying to wrap my head around the Σ type by playing with a filterVec function, analogous to the filter function available for lists. See my implementation below:
-- Some imports we will need later
open import Data.Bool
open import Data.Nat
open import Data.Product
open import Data.Vec
-- The filterVec function
filterVec : {n : ℕ} -> (ℕ -> Bool) -> Vec ℕ n -> Σ ℕ (λ length → Vec ℕ length)
filterVec _ [] = 0 , []
filterVec f (x ∷ xs) with filterVec f xs
... | length , filtered = if f x then (suc length , x ∷ filtered) else (length , filtered)
Happy with my function, I decided to test it
dummyVec : Vec ℕ 5
dummyVec = 1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ []
dummyFn : Vec ℕ 5
dummyFn with filterVec (λ _ → true) dummyVec
dummyFn | length , xs = {!!} -- I would like to return xs here, but Agda throws a type error
For some reason, Agda is unable to tell that length is 5, so it doesn't allow me to return xs. However, the code below does work:
open import Relation.Binary.PropositionalEquality
dummyProof : proj₁ (filterVec (λ _ → true) dummyVec) ≡ 5
dummyProof = refl
I am utterly confused by this behavior. In the case of dummyFn, Agda cannot tell that the length is 5, but in the case of dummyProof, Agda proves it without any problem.
What am I missing? How can I get dummyFn to work?
with binds a value to a name. If you want to compute, you can use let:
dummyFn′ : Vec ℕ 5
dummyFn′ = let length , xs = filterVec (λ _ → true) dummyVec
in xs
Let's look at the following example:
dummy : Set
dummy with 5
dummy | x = {!!}
Should the x magically reduce to 5 in the hole? No. You've given 5 another name — x, and those expressions are neither judgementally nor propositionally equal.
Now in your example:
dummyFn : Vec ℕ 5
dummyFn with filterVec (λ _ → true) dummyVec
dummyFn | length , xs = {!!}
You've given the length name to the length of xs. And it does not reduce to 5. It's a name. If you want both to give a name and to remember what the name is for, there is the inspect idiom for this:
dummyFn′′ : Vec ℕ 5
dummyFn′′ with filterVec (λ _ → true) dummyVec | inspect (filterVec (λ _ → true)) dummyVec
dummyFn′′ | length , xs | [ refl ] = xs

How to prove unfold-reverse for Vec?

The Agda standard library has a few properties on how reverse and _++_ work on List. Trying to transfer these proofs to Vec appears to be non-trivial (disregarding universes):
open import Data.Nat
open import Data.Vec
open import Relation.Binary.HeterogeneousEquality
unfold-reverse : {A : Set} → (x : A) → {n : ℕ} → (xs : Vec A n) →
reverse (x ∷ xs) ≅ reverse xs ++ [ x ]
TL;DR: How to prove unfold-reverse?
The rest of this question outlines approaches to doing so and explains what problems surface.
The type of this property is very similar to the List counter part in Data.List.Properties. The proof involves a helper which roughly translates to:
open import Function
helper : ∀ {n m} → (xs : Vec A n) → (ys : Vec A m) →
foldl (Vec A ∘ (flip _+_ n)) (flip _∷_) xs ys ≅ reverse ys ++ xs
Trying to insert this helper in unfold-reverse fails, because the left hand reverse is a foldl application with Vec A ∘ suc as first argument whereas the left hand side of helper has a foldl application with Vec A ∘ (flip _+_ 1) as first argument. Even though suc ≗ flip _+_ 1 is readily available from Data.Nat.Properties.Simple, it cannot be used here as cong would need a non-pointwise equality here and we don't have extensionality without further assumptions.
Removing the flip from flip _+_ n in helper yields a type error, so that is no option either.
Any other ideas?
The Data.Vec.Properties module contains this function:
foldl-cong : ∀ {a b} {A : Set a}
{B₁ : ℕ → Set b}
{f₁ : ∀ {n} → B₁ n → A → B₁ (suc n)} {e₁}
{B₂ : ℕ → Set b}
{f₂ : ∀ {n} → B₂ n → A → B₂ (suc n)} {e₂} →
(∀ {n x} {y₁ : B₁ n} {y₂ : B₂ n} →
y₁ ≅ y₂ → f₁ y₁ x ≅ f₂ y₂ x) →
e₁ ≅ e₂ →
∀ {n} (xs : Vec A n) →
foldl B₁ f₁ e₁ xs ≅ foldl B₂ f₂ e₂ xs
foldl-cong _ e₁=e₂ [] = e₁=e₂
foldl-cong {B₁ = B₁} f₁=f₂ e₁=e₂ (x ∷ xs) =
foldl-cong {B₁ = B₁ ∘ suc} f₁=f₂ (f₁=f₂ e₁=e₂) xs
Here is more or less elaborated solution:
unfold-reverse : {A : Set} → (x : A) → {n : ℕ} → (xs : Vec A n) →
reverse (x ∷ xs) ≅ reverse xs ++ (x ∷ [])
unfold-reverse x xs = begin
foldl (Vec _ ∘ _+_ 1) (flip _∷_) (x ∷ []) xs
≅⟨ (foldl-cong
{B₁ = Vec _ ∘ _+_ 1}
{f₁ = flip _∷_}
{e₁ = x ∷ []}
{B₂ = Vec _ ∘ flip _+_ 1}
{f₂ = flip _∷_}
{e₂ = x ∷ []}
(λ {n} {a} {as₁} {as₂} as₁≅as₂ -> {!!})
refl
xs) ⟩
foldl (Vec _ ∘ flip _+_ 1) (flip _∷_) (x ∷ []) xs
≅⟨ helper (x ∷ []) xs ⟩
reverse xs ++ x ∷ []
∎
Note, that only B₁ and B₂ are distinct in the arguments of the foldl-cong function. After simplifying context in the hole we have
Goal: a ∷ as₁ ≅ a ∷ as₂
————————————————————————————————————————————————————————————
as₁≅as₂ : as₁ ≅ as₂
as₂ : Vec A (n + 1)
as₁ : Vec A (1 + n)
a : A
n : ℕ
A : Set
So we need to prove, that at each recursive call adding an element to an accumulator of type Vec A (n + 1) is equal to adding an element to an accumulator of type Vec A (1 + n), and then results of two foldls are equal. The proof itself is simple. Here is the whole code:
open import Function
open import Relation.Binary.HeterogeneousEquality
open import Data.Nat
open import Data.Vec
open import Data.Nat.Properties.Simple
open import Data.Vec.Properties
open ≅-Reasoning
postulate
helper : ∀ {n m} {A : Set} (xs : Vec A n) (ys : Vec A m)
-> foldl (Vec A ∘ flip _+_ n) (flip _∷_) xs ys ≅ reverse ys ++ xs
cong' : ∀ {α β γ} {I : Set α} {i j : I}
-> (A : I -> Set β) {B : {k : I} -> A k -> Set γ} {x : A i} {y : A j}
-> i ≅ j
-> (f : {k : I} -> (x : A k) -> B x)
-> x ≅ y
-> f x ≅ f y
cong' _ refl _ refl = refl
unfold-reverse : {A : Set} → (x : A) → {n : ℕ} → (xs : Vec A n) →
reverse (x ∷ xs) ≅ reverse xs ++ (x ∷ [])
unfold-reverse x xs = begin
foldl (Vec _ ∘ _+_ 1) (flip _∷_) (x ∷ []) xs
≅⟨ (foldl-cong
{B₁ = Vec _ ∘ _+_ 1}
{f₁ = flip _∷_}
{e₁ = x ∷ []}
{B₂ = Vec _ ∘ flip _+_ 1}
{f₂ = flip _∷_}
{e₂ = x ∷ []}
(λ {n} {a} {as₁} {as₂} as₁≅as₂ -> begin
a ∷ as₁
≅⟨ cong' (Vec _) (sym (≡-to-≅ (+-comm n 1))) (_∷_ a) as₁≅as₂ ⟩
a ∷ as₂
∎)
refl
xs) ⟩
foldl (Vec _ ∘ flip _+_ 1) (flip _∷_) (x ∷ []) xs
≅⟨ helper (x ∷ []) xs ⟩
reverse xs ++ x ∷ []
∎

Termination check on list merge

Agda 2.3.2.1 can't see that the following function terminates:
open import Data.Nat
open import Data.List
open import Relation.Nullary
merge : List ℕ → List ℕ → List ℕ
merge (x ∷ xs) (y ∷ ys) with x ≤? y
... | yes p = x ∷ merge xs (y ∷ ys)
... | _ = y ∷ merge (x ∷ xs) ys
merge xs ys = xs ++ ys
Agda wiki says that it's OK for the termination checker if the arguments on recursive calls decrease lexicographically. Based on that it seems that this function should also pass. So what am I missing here? Also, is it maybe OK in previous versions of Agda? I've seen similar code on the Internet and no one mentioned termination issues there.
I cannot give you the reason why exactly this happens, but I can show you how to cure the symptoms. Before I start: This is a known problem with the termination checker. If you are well-versed in Haskell, you could take a look at the source.
One possible solution is to split the function into two: first one for the case where the first argument gets smaller and second for the second one:
mutual
merge : List ℕ → List ℕ → List ℕ
merge (x ∷ xs) (y ∷ ys) with x ≤? y
... | yes _ = x ∷ merge xs (y ∷ ys)
... | no _ = y ∷ merge′ x xs ys
merge xs ys = xs ++ ys
merge′ : ℕ → List ℕ → List ℕ → List ℕ
merge′ x xs (y ∷ ys) with x ≤? y
... | yes _ = x ∷ merge xs (y ∷ ys)
... | no _ = y ∷ merge′ x xs ys
merge′ x xs [] = x ∷ xs
So, the first function chops down xs and once we have to chop down ys, we switch to the second function and vice versa.
Another (perhaps surprising) option, which is also mentioned in the issue report, is to introduce the result of recursion via with:
merge : List ℕ → List ℕ → List ℕ
merge (x ∷ xs) (y ∷ ys) with x ≤? y | merge xs (y ∷ ys) | merge (x ∷ xs) ys
... | yes _ | r | _ = x ∷ r
... | no _ | _ | r = y ∷ r
merge xs ys = xs ++ ys
And lastly, we can perform the recursion on Vectors and then convert back to List:
open import Data.Vec as V
using (Vec; []; _∷_)
merge : List ℕ → List ℕ → List ℕ
merge xs ys = V.toList (go (V.fromList xs) (V.fromList ys))
where
go : ∀ {n m} → Vec ℕ n → Vec ℕ m → Vec ℕ (n + m)
go {suc n} {suc m} (x ∷ xs) (y ∷ ys) with x ≤? y
... | yes _ = x ∷ go xs (y ∷ ys)
... | no _ rewrite lem n m = y ∷ go (x ∷ xs) ys
go xs ys = xs V.++ ys
However, here we need a simple lemma:
open import Relation.Binary.PropositionalEquality
lem : ∀ n m → n + suc m ≡ suc (n + m)
lem zero m = refl
lem (suc n) m rewrite lem n m = refl
We could also have go return List directly and avoid the lemma altogether:
merge : List ℕ → List ℕ → List ℕ
merge xs ys = go (V.fromList xs) (V.fromList ys)
where
go : ∀ {n m} → Vec ℕ n → Vec ℕ m → List ℕ
go (x ∷ xs) (y ∷ ys) with x ≤? y
... | yes _ = x ∷ go xs (y ∷ ys)
... | no _ = y ∷ go (x ∷ xs) ys
go xs ys = V.toList xs ++ V.toList ys
The first trick (i.e. split the function into few mutually recursive ones) is actually quite good to remember. Since the termination checker doesn't look inside the definitions of other functions you use, it rejects a great deal of perfectly fine programs, consider:
data Rose {a} (A : Set a) : Set a where
[] : Rose A
node : A → List (Rose A) → Rose A
And now, we'd like to implement mapRose:
mapRose : ∀ {a b} {A : Set a} {B : Set b} →
(A → B) → Rose A → Rose B
mapRose f [] = []
mapRose f (node t ts) = node (f t) (map (mapRose f) ts)
The termination checker, however, doesn't look inside the map to see if it doesn't do anything funky with the elements and just rejects this definition. We must inline the definition of map and write a pair of mutually recursive functions:
mutual
mapRose : ∀ {a b} {A : Set a} {B : Set b} →
(A → B) → Rose A → Rose B
mapRose f [] = []
mapRose f (node t ts) = node (f t) (mapRose′ f ts)
mapRose′ : ∀ {a b} {A : Set a} {B : Set b} →
(A → B) → List (Rose A) → List (Rose B)
mapRose′ f [] = []
mapRose′ f (t ∷ ts) = mapRose f t ∷ mapRose′ f ts
Usually, you can hide most of the mess in a where declaration:
mapRose : ∀ {a b} {A : Set a} {B : Set b} →
(A → B) → Rose A → Rose B
mapRose {A = A} {B = B} f = go
where
go : Rose A → Rose B
go-list : List (Rose A) → List (Rose B)
go [] = []
go (node t ts) = node (f t) (go-list ts)
go-list [] = []
go-list (t ∷ ts) = go t ∷ go-list ts
Note: Declaring signatures of both functions before they are defined can be used instead of mutual in newer versions of Agda.
Update: The development version of Agda got an update to the termination checker, I'll let the commit message and release notes speak for themselves:
A revision of call graph completion that can deal with arbitrary termination depth.
This algorithm has been sitting around in MiniAgda for some time,
waiting for its great day. It is now here!
Option --termination-depth can now be retired.
And from the release notes:
Termination checking of functions defined by 'with' has been improved.
Cases which previously required --termination-depth (now obsolete!)
to pass the termination checker (due to use of 'with') no longer
need the flag. For example
merge : List A → List A → List A
merge [] ys = ys
merge xs [] = xs
merge (x ∷ xs) (y ∷ ys) with x ≤ y
merge (x ∷ xs) (y ∷ ys) | false = y ∷ merge (x ∷ xs) ys
merge (x ∷ xs) (y ∷ ys) | true = x ∷ merge xs (y ∷ ys)
This failed to termination check previously, since the 'with'
expands to an auxiliary function merge-aux:
merge-aux x y xs ys false = y ∷ merge (x ∷ xs) ys
merge-aux x y xs ys true = x ∷ merge xs (y ∷ ys)
This function makes a call to merge in which the size of one of the
arguments is increasing. To make this pass the termination checker
now inlines the definition of merge-aux before checking, thus
effectively termination checking the original source program.
As a result of this transformation doing 'with' on a variable no
longer preserves termination. For instance, this does not
termination check:
bad : Nat → Nat
bad n with n
... | zero = zero
... | suc m = bad m
And indeed, your original function now passes the termination check!

≡-Reasoning and 'with' patterns

I was proving some properties of filter and map, everything went quite good until I stumbled on this property: filter p (map f xs) ≡ map f (filter (p ∘ f) xs). Here's a part of the code that's relevant:
open import Relation.Binary.PropositionalEquality
open import Data.Bool
open import Data.List hiding (filter)
import Level
filter : ∀ {a} {A : Set a} → (A → Bool) → List A → List A
filter _ [] = []
filter p (x ∷ xs) with p x
... | true = x ∷ filter p xs
... | false = filter p xs
Now, because I love writing proofs using the ≡-Reasoning module, the first thing I tried was:
open ≡-Reasoning
open import Function
filter-map : ∀ {a b} {A : Set a} {B : Set b}
(xs : List A) (f : A → B) (p : B → Bool) →
filter p (map f xs) ≡ map f (filter (p ∘ f) xs)
filter-map [] _ _ = refl
filter-map (x ∷ xs) f p with p (f x)
... | true = begin
filter p (map f (x ∷ xs))
≡⟨ refl ⟩
f x ∷ filter p (map f xs)
-- ...
But alas, that didn't work. After trying for one hour, I finally gave up and proved it in this way:
filter-map (x ∷ xs) f p with p (f x)
... | true = cong (λ a → f x ∷ a) (filter-map xs f p)
... | false = filter-map xs f p
Still curious about why going through ≡-Reasoning didn't work, I tried something very trivial:
filter-map-def : ∀ {a b} {A : Set a} {B : Set b}
(x : A) xs (f : A → B) (p : B → Bool) → T (p (f x)) →
filter p (map f (x ∷ xs)) ≡ f x ∷ filter p (map f xs)
filter-map-def x xs f p _ with p (f x)
filter-map-def x xs f p () | false
filter-map-def x xs f p _ | true = -- not writing refl on purpose
begin
filter p (map f (x ∷ xs))
≡⟨ refl ⟩
f x ∷ filter p (map f xs)
∎
But typechecker doesn't agree with me. It would seem that the current goal remains filter p (f x ∷ map f xs) | p (f x) and even though I pattern matched on p (f x), filter just won't reduce to f x ∷ filter p (map f xs).
Is there a way to make this work with ≡-Reasoning?
Thanks!
The trouble with with-clauses is that Agda forgets the information it learned from pattern match unless you arrange beforehand for this information to be preserved.
More precisely, when Agda sees a with expression clause, it replaces all the occurences of expression in the current context and goal with a fresh variable w and then gives you that variable with updated context and goal into the with-clause, forgetting everything about its origin.
In your case, you write filter p (map f (x ∷ xs)) inside the with-block, so it goes into scope after Agda has performed the rewriting, so Agda has already forgotten the fact that p (f x) is true and does not reduce the term.
You can preserve the proof of equality by using one of the "Inspect"-patterns from the standard library, but I'm not sure how it can be useful in your case.

Resources