[Agda] Termination problems with "with" and recursion

Andreas Abel andreas.abel at ifi.lmu.de
Tue Dec 3 17:50:50 CET 2013


[CC to Ulf]

There are two issues here.

First, your size assignment for merge does not work.  You are saying 
both argument have a common upper bound that is decreased in each 
recursive call.  This is not the case, since only one of the two heaps 
gets smaller each time.  So you need to have two sizes:

module ShouldSucceed where

   merge : {i j : Size} {p : Nat} → Heap {i} p → Heap {j} p → Heap {∞} p
   merge empty h2 = h2
   merge h1 empty = h1
   merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 
r-rank p2≥p l2 r2) with order p1 p2
   merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 
r-rank p2≥p l2 r2) | le p1≤p2 = makeT p1 p1≥p l1 (merge {i} {↑ j} r1 
(node p2 r-rank p1≤p2 l2 r2))
   merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 
r-rank p2≥p l2 r2) | ge p1≥p2 = makeT p2 p2≥p l2 (merge {↑ i} {j} (node 
p1 l-rank p1≥p2 l1 r1) r2)

This should work.  However, it does not, which seems to be a bug, and I 
filed an issue.  (See agda issues 59).

If you expand the "with" out manually (the new command C-c C-h comes in 
very handy here), you get something that is accepted by the termination 
checker:

module Succeeds where
  mutual
   merge : {i j : Size} {p : Nat} → Heap {i} p → Heap {j} p → Heap {∞} p
   merge empty h2 = h2
   merge h1 empty = h1
   merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 
r-rank p2≥p l2 r2) =
     merge' {i} {j} p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (order p1 p2)

   merge' : ∀ {i} {j} {p} →
            ∀ p1 → Nat → p1 ≥ p → Heap {i} p1 → Heap {i} p1 →
            ∀ p2 → Nat → p2 ≥ p → Heap {j} p2 → Heap {j} p2 → Order p1 
p2 → Heap {∞} p
   merge' {i} {j} p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (le p1≤p2) = 
makeT p1 p1≥p l1 (merge {i} {↑ j} r1 (node p2 r-rank p1≤p2 l2 r2))
   merge' {i} {j} p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (ge p1≥p2) = 
makeT p2 p2≥p l2 (merge {↑ i} {j} (node p1 l-rank p1≥p2 l1 r1) r2)

Cheers,
Andreas

On 02.12.2013 14:09, Jan Stolarek wrote:
> Ok, I think I need some more assistance :-)
>
> Now let's say I have this definition:
>
> data Heap : {i : Size} → Priority → Set where
>    empty : {i : Size} {n : Priority} → Heap {↑ i} n
>    node  : {i : Size} {n : Priority} → (p : Priority) → Rank → p ≥ n →
>            Heap {i} p → Heap {i} p → Heap {↑ i} n
>
> ≥ is evidence that p >= n. If I now extend merge with evidence like this:
>
> merge : {i : Size} {p : Nat} → Heap {i} p → Heap {i} p → Heap p
> merge empty h2 = h2
> merge h1 empty = h1
> merge .{↑ i} (node .{i} p1 l-rank p1≥p l1 r1) (node {i} p2 r-rank p2≥p l2 r2) with order p1 p2
> merge .{↑ i} (node .{i} p1 l-rank p1≥p l1 r1) (node {i} p2 r-rank p2≥p l2 r2) | le p1≤p2
>    = makeT p1 p1≥p l1 (merge r1 (node p2 r-rank p1≤p2 l2 r2))
> merge .{↑ i} (node .{i} p1 l-rank p1≥p l1 r1) (node {i} p2 r-rank p2≥p l2 r2) | ge p1≥p2
>    = makeT p2 p2≥p l2 (merge r2 (node p1 l-rank p1≥p2 l1 r1))
>
> The termination checker complains on the second call to merge, but does not complain on the first
> one. I am puzzled. Andreas, can you explain why the first case works and the second doesn't? They
> seem to be symmetric. I'm attaching source file with definitions that I'm using.
>
> Janek
>
> Dnia poniedziałek, 2 grudnia 2013, Andreas Abel napisał:
>> Here you go:
>> Note that unconstraint sizes default to \inf, so you need to give sizes
>> in type signatures where sizes are important, e.g. in merge.
>>
>> {-# OPTIONS --sized-types #-}
>>
>> open import Size
>> open import Data.Bool
>>
>> module _ (Rank Priority : Set) (_<_ : Priority → Priority → Bool) where
>>
>> data Heap : {i : Size} → Set where
>>     empty : {i : Size} → Heap {↑ i}
>>     node  : {i : Size} → Priority → Rank → Heap {i} → Heap {i} → Heap {↑ i}
>>
>> module _ (makeT : Priority → Heap → Heap → Heap) where
>>
>>     merge : {i : Size} → Heap {i} → Heap {i} → Heap
>>     merge empty h2 = h2
>>     merge h1 empty = h1
>>     merge (node p1 w1 l1 r1) (node p2 w2 l2 r2)  with p1 < p2
>>     merge (node p1 w1 l1 r1) (node p2 w2 l2 r2)  | true  =
>>       makeT p1 l1 (merge r1 (node p2 w2 l2 r2))
>>     merge (node p1 w1 l1 r1) (node p2 w2 l2 r2)  | false =
>>       makeT p2 l2 (merge (node p1 w1 l1 r1) r2)
>>
>> Depending on what makeT is, you might be able to give a more precise
>> type to merge.
>>
>> On 02.12.2013 12:38, Jan Stolarek wrote:
>>> Ok, I think I need some help with Sized types. I have a datatype
>>> representing a Heap:
>>>
>>> data Heap : {i : Size} → Set where
>>>     empty : {i : Size} → Heap {↑ i}
>>>     node  : {i : Size} → Priority → Rank → Heap {i} → Heap {i} → Heap {↑
>>> i}
>>>
>>> And I need to use sized types in merge function:
>>>
>>> merge : {i : Size} → Heap → Heap → Heap
>>> merge h1 h2 with h1 | h2
>>> merge h1 h2 | empty | _ = h2
>>> merge h1 h2 | _ | empty = h1
>>> merge h1 h2  | (node p1 w1 l1 r1)  | (node p2 w2 l2 r2)  with p1 < p2
>>> merge h1 h2  | (node p1 w1 l1 r1)  | (node p2 w2 l2 r2)  | true  = makeT
>>> p1 l1 (merge r1 h2) merge h1 h2  | (node p1 w1 l1 r1)  | (node p2 w2 l2
>>> r2)  | false = makeT p2 l2 (merge h1 r2)
>>>
>>> Can I please have a hint how to do that? I tried adding annotation
>>> according to this presentation:
>>>
>>> www2.tcs.ifi.lmu.de/~abel/talkAIM2008Sendai.pdf‎
>>>
>>> but with no success :-/ I keep getting errors like i != inf.
>>>
>>> Janek
>
>


-- 
Andreas Abel  <><      Du bist der geliebte Mensch.

Theoretical Computer Science, University of Munich
Oettingenstr. 67, D-80538 Munich, GERMANY

andreas.abel at ifi.lmu.de
http://www2.tcs.ifi.lmu.de/~abel/
-------------- next part --------------
{-# OPTIONS --sized-types #-}
{-# OPTIONS --show-implicit #-}
{-# OPTIONS -v term:20  #-}
module SizedMerge where

open import Size

data Bool : Set where
  false : Bool
  true  : Bool

data Nat : Set where
  zero : Nat
  suc  : Nat → Nat

Priority : Set
Priority = Nat

Rank : Set
Rank = Nat

data _≥_ : Nat → Nat → Set where
  ge0 : {y : Nat}                   → y     ≥ zero
  geS : {x : Nat} {y : Nat} → x ≥ y → suc x ≥ suc y

infixl 4 _≥_

_+_ : Nat → Nat → Nat
zero  + m = m
suc n + m = suc (n + m)

infixl 6 _+_

data Order : Nat → Nat → Set where
  ge : {x : Nat} {y : Nat} → x ≥ y → Order x y
  le : {x : Nat} {y : Nat} → y ≥ x → Order x y

order : (a : Nat) → (b : Nat) → Order a b
order a       zero    = ge ge0
order zero    (suc b) = le ge0
order (suc a) (suc b) with order a b
order (suc a) (suc b) | ge ageb = ge (geS ageb)
order (suc a) (suc b) | le bgea = le (geS bgea)

data Heap : {i : Size} → Priority → Set where
  empty : {i : Size} {n : Priority} → Heap {↑ i} n
  node  : {i : Size} {n : Priority} → (p : Priority) → Rank → p ≥ n →
          Heap {i} p → Heap {i} p → Heap {↑ i} n

_≥ℕ_ : Nat → Nat → Bool
m     ≥ℕ zero  = true
zero  ≥ℕ suc n = false
suc m ≥ℕ suc n = m ≥ℕ n

rank : {n : Nat} → Heap n → Nat
rank empty            = zero
rank (node _ r _ _ _) = r

makeT : {n : Nat} → (p : Priority) → p ≥ n → Heap p → Heap p → Heap n
makeT p pgen l r with rank l ≥ℕ rank r
makeT p pgen l r | true  = node p (suc (rank l + rank r)) pgen l r
makeT p pgen l r | false = node p (suc (rank l + rank r)) pgen r l


module Fails where

  merge : {i : Size} {p : Nat} → Heap {i} p → Heap {i} p → Heap p
  merge empty h2 = h2
  merge h1 empty = h1
  merge .{↑ i} (node .{i} p1 l-rank p1≥p l1 r1) (node {i} p2 r-rank p2≥p l2 r2) with order p1 p2
  merge .{↑ i} (node .{i} p1 l-rank p1≥p l1 r1) (node {i} p2 r-rank p2≥p l2 r2) | le p1≤p2
    = makeT p1 p1≥p l1 (merge {↑ i} r1 (node p2 r-rank p1≤p2 l2 r2))
  merge .{↑ i} (node .{i} p1 l-rank p1≥p l1 r1) (node {i} p2 r-rank p2≥p l2 r2) | ge p1≥p2
    = makeT p2 p2≥p l2 (merge {↑ i} r2 (node p1 l-rank p1≥p2 l1 r1))

module ShouldSucceed where

  merge : {i j : Size} {p : Nat} → Heap {i} p → Heap {j} p → Heap {∞} p
  merge empty h2 = h2
  merge h1 empty = h1
  merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 r-rank p2≥p l2 r2) with order p1 p2
  merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 r-rank p2≥p l2 r2) | le p1≤p2 = makeT p1 p1≥p l1 (merge {i} {↑ j} r1 (node p2 r-rank p1≤p2 l2 r2))
  merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 r-rank p2≥p l2 r2) | ge p1≥p2 = makeT p2 p2≥p l2 (merge {↑ i} {j} (node p1 l-rank p1≥p2 l1 r1) r2)


module Succeeds where
 mutual
  merge : {i j : Size} {p : Nat} → Heap {i} p → Heap {j} p → Heap {∞} p
  merge empty h2 = h2
  merge h1 empty = h1
  merge .{↑ i} .{↑ j} (node {i} p1 l-rank p1≥p l1 r1) (node {j} p2 r-rank p2≥p l2 r2) =
    merge' {i} {j} p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (order p1 p2)

  merge' : ∀ {i} {j} {p} →
           ∀ p1 → Nat → p1 ≥ p → Heap {i} p1 → Heap {i} p1 →
           ∀ p2 → Nat → p2 ≥ p → Heap {j} p2 → Heap {j} p2 → Order p1 p2 → Heap {∞} p
  merge' {i} {j} p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (le p1≤p2) = makeT p1 p1≥p l1 (merge {i} {↑ j} r1 (node p2 r-rank p1≤p2 l2 r2))
  merge' {i} {j} p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (ge p1≥p2) = makeT p2 p2≥p l2 (merge {↑ i} {j} (node p1 l-rank p1≥p2 l1 r1) r2)


module Succeeds' where
 mutual
  merge : ∀ {i j p} → Heap {i} p → Heap {j} p → Heap {∞} p
  merge empty h2 = h2
  merge h1 empty = h1
  merge  (node p1 l-rank p1≥p l1 r1) (node p2 r-rank p2≥p l2 r2) =
    merge' p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (order p1 p2)

  merge' : ∀ {i j p} →
           ∀ p1 → Nat → p1 ≥ p → Heap {i} p1 → Heap {i} p1 →
           ∀ p2 → Nat → p2 ≥ p → Heap {j} p2 → Heap {j} p2 → Order p1 p2 → Heap {∞} p
  merge' p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (le p1≤p2) = makeT p1 p1≥p l1 (merge r1 (node p2 r-rank p1≤p2 l2 r2))
  merge' p1 l-rank p1≥p l1 r1 p2 r-rank p2≥p l2 r2 (ge p1≥p2) = makeT p2 p2≥p l2 (merge (node p1 l-rank p1≥p2 l1 r1) r2)


More information about the Agda mailing list