3
open import Data.Nat using (ℕ;suc;zero)
open import Data.Rational
open import Data.Product
open import Relation.Nullary
open import Data.Bool using (Bool;false;true)

halve : ℕ → ℚ
halve zero = 1ℚ
halve (suc p) = ½ * halve p

∃-halve : ∀ {a b} → 0ℚ < a → a < b → ∃[ p ] (halve p * b < a)
∃-halve {a} {b} 0<a a<b = h 1 where
  h : ℕ → ∃[ p ] (halve p * b < a)
  h p with halve p * b <? a
  h p | .true because ofʸ b'<a = p , b'<a
  h p | .false because ofⁿ ¬b'<a = h (suc p)

The termination checker fails in that last case and it is no wonder as the recursion obviously is neither well funded nor structural. Nevertheless, I am quite sure this should be valid, but have no idea how to prove the termination of ∃-halve. Any advice for how this might be done?

Marko Grdinić
  • 3,798
  • 3
  • 18
  • 21
  • Is your example self contained ? I just copy-pasted it and it does not typecheck. And also, what do you want to achieve with "halve" because it looks to me that you compute `1/2^n` which does not match the name of the function. – MrO Dec 10 '19 at 17:11
  • Does it fail for you because it fails termination checking or because of something else? The `halve` function is intended to compute `1/2^n` as you've noted. Think of it as iterated halving. – Marko Grdinić Dec 10 '19 at 17:40
  • You're trying to solve 1/2^n * b < a, which can be rearranged to n < log₂(b/a). This means that you'll have to compute ⌊log₂(b/a)⌋, or something smaller than it. – mudri Dec 11 '19 at 09:39
  • 1
    Not the direction I want to go in as it would involve me implementing `log₂`. If I do that, there is no doubt I will run into termination issues with `log₂`. Since I am working over rationals instead of reals, `log₂` will necessarily have to have precision as an argument (similarly to [sqrt here](https://github.com/agda/agda-stdlib/issues/990)) and I will be stuck trying to prove that some precision exists where `n < log₂(b/a)` holds. – Marko Grdinić Dec 11 '19 at 14:42

1 Answers1

6

When you’re stuck on a lemma like this, a good general principle is to forget Agda and its technicalities for a minute. How would you prove it in ordinary human-readable mathematical prose, in as elementary way as possible?

Your “iterated halving” function is computing b/(2^p). So you’re trying to show: for any positive rationals a, b, there is some natural p such that b/(2^p) < a. This inequality is equivalent to 2^p > b/a. You can break this down into two steps: find some natural nb/a, and then find some p such that 2^p > n.

As mentioned in comments, a natural way to do find such numbers would be to implement the ceiling function and the log_2 function. But as you say, those would be rather a lot of work, and you don’t need them here; you just need existence of such numbers. So you can do the above proof in three steps, each of which is elementary enough for a self-contained Agda proof, requiring only very basic algebraic facts as background:

  • Lemma 1: for any rational q, there’s some natural n > q. (Proof: use the definition of the ordering on rationals, and a little bit of algebra.)

  • Lemma 2: for any natural n, there’s some natural p such that 2^p > n. (Proof: take e.g. p := (n+1); prove by induction on n that 2^(n+1) > n.)

  • Lemma 3: these together imply the theorem about halving that you wanted. (Proof: a bit of algebra with rationals, showing that the b/(2^p) < a is equivalent to 2^p > b/a, and showing that your iterated-halving function gives b/2^p.)

PLL
  • 1,572
  • 1
  • 13
  • 21