[Agda] WellFounded usage
Sergei Meshveliani
mechvel at botik.ru
Mon Aug 6 14:20:24 CEST 2018
Dear all,
I have a certain problem with termination proof.
Consider the example: division with remainder for binary natural
numbers (the code is contrived).
-------------------------------------------------------------------
record DivMod (dividend divisor : Bin) : Set where
constructor result
field
quotient : Bin
remainder : Bin
equality : dividend ≡ remainder + quotient * divisor
rem<divisor : remainder < divisor
-- suc, +, ∸; *, < are of Bin, 1+, +n, ∸n; *n, <n are of ℕ.
∣_∣ : Bin → ℕ
∣ x ∣ = number of bits
divMod : (a b : Bin) → b ≢ 0# → DivMod a b
divMod a b b≢0 =
aux a (toℕ a) ≤-refl
where
aux : (a : Bin) → cnt → toℕ a ≤ cnt → DivMod a b
aux a 0 aN≤0 =
result 0# 0# a≡0+0*b 0<b
where
a≡0 = derived from aN≤0
aux 0# _ _ = result 0# 0# 0≡0+0*b 0<b
aux a (1+ cnt) aN≤1+cnt = -- here a ≢ 0#
let
aN = toℕ a
e = ∣ a ∣ ∸n ∣ b ∣ ...
d = (2^ e) * b -- 2^ e is by prepending several
-- zero bits.
a' = a ∸ d
a'N = toℕ a'
a'N≤cnt : a'N ≤n cnt
a'N≤cnt =
because a, d ≢ 0#, and toℕ∸homo.
(result q r a'≡r+q*b r<b) = aux a' cnt a'N≤cnt
in
restore a b q r a' r<b
------------------------------------------------------------------
This means that divMod a b is reduced to (divMod (a ∸ d) b),
where d = 2^e * b for a certain appropriate e : ℕ.
The counter cnt = (toℕ a) : ℕ is reduced to a smaller value by this
step.
Due to the structural decrement (suc cnt) -> cnt, Agda decides that
the recursion is terminating.
But this may lead to exponential performance cost, because the unary
representation of cnt is large, and the evaluation (suc cnt) -> snt
takes place not only in the proof part but also in regular evaluation.
To fix this, I change the counter to
cnt = ∣ a ∣ : ℕ = number of bits in a.
Then one needs to prove messy lemmata in order to prove that
∣ a ∣ >n ∣ a' ∣ in the loop.
The question is
does there exist a way for this example to join
* a normal code performance (as in the second method version),
* simple termination proof
?
I see in Standard library Induction/WellFounded.agda.
It is difficult to understand of how to apply this
(the impression is that it will complicate the program).
Can this tool be used to solve the above problem with divMod for Bin ?
Somehow to prove that _>_ is well-founded on Bin
(it is useful for many other programs),
use the tools from WellFounded.agda, and prove termination of divMod
in a simple way and without loosing run-time performance.
Can anybody, please, demonstrate, how this will change the above code
for divMod ?
Another question
----------------
Has it sense the language semantic extension with WellFoundedOrder ?
The property IsWellFoundedOrder concerns any StrictPartialOrder,
and its simple definition is
appended to this letter.
Imagine that the type checker of Agda-extended
sees in the above loop
a a' : Bin and a>a' : a > a',
sees the instance of StrictPartialOrder for _≡_ and _>_ on Bin,
sees the proof for IsWellFoundedOrder for this instance,
and concludes that divMod is terminating -- by the _implicit axiom_
of termination by a well-founded ordering.
With this, the proofs will be much simpler, simpler than with applying
the constructs of Induction of Library.
Agda is able to automatically find an argument in recursion that
decreases structurally. Similarly can it be able to find the argument
decreasing by _<_ and fit for well-founded recursion?
Agda sees that a is replaced with a' in the call, and it needs to
find where in the scope it is proved a > a'. At least this place for
a>a' can be marked, may be, by some pragma.
Thank you in advance for explanation,
------
Sergei
-------------- next part --------------
module WellFounded-2 where
open import Function using (flip; _∘_)
open import Relation.Nullary using (¬_)
open import Relation.Binary using (Rel; Setoid; StrictPartialOrder)
open import Relation.Binary.PropositionalEquality as PE using (_≡_)
open import Data.Product using (∃)
open import Data.Nat using (ℕ; suc)
--****************************************************************************
Sequence : ∀ {α} → (A : Set α) → Set α -- infinite sequences in A
Sequence A = ℕ → A
module TwoSetoids {α α= β β=} (A : Setoid α α=) (B : Setoid β β=)
where
open Setoid A using (_≈_) renaming (Carrier to C)
open Setoid B using () renaming (Carrier to C'; _≈_ to _≈'_)
Injective : (C → C') → Set _
Injective f =
(x y : C) → f x ≈' f y → x ≈ y
------------------------------------------------------------------------------
module _ {α α= α<} (PO : StrictPartialOrder α α= α<)
where
open StrictPartialOrder PO using (_≈_; _<_; isEquivalence)
renaming (Carrier to C)
setoid : Setoid α α=
setoid = record { Carrier = C; _≈_ = _≈_;
isEquivalence = isEquivalence }
_>_ : Rel C α<
_>_ = flip _<_
_≯_ : Rel C α<
_≯_ x = ¬_ ∘ _>_ x
IsDecreasing : Sequence C → Set _
IsDecreasing f =
∀ n → f n > f (suc n)
natSetoid = PE.setoid ℕ
module Rel-Nat-C = TwoSetoids natSetoid setoid
open Rel-Nat-C using (Injective)
IsWellFoundedOrder : Set _
IsWellFoundedOrder = (f : Sequence C) → Injective f →
¬ IsDecreasing f
More information about the Agda
mailing list