[Agda] summing rationals

Sergei Meshveliani mechvel at botik.ru
Sun Jun 25 16:26:55 CEST 2017


On Sun, 2017-06-25 at 09:46 +0200, Ulf Norell wrote:
> The rationals are not very well-developed in agda-prelude.
> Nevertheless I get unsurprising performance compared to ghc (~2min for
> n = 20,000 on my machine) with the following benchmark:
> 
> 
> module Harmonic where
> 
> 
> open import Prelude
> open import Numeric.Rational
> 
> 
> -- sum [ 1/k | k <- [1..n] ]
> harmonic : Nat → Rational
> harmonic 0 = 0
> harmonic (suc n) = sum (map (λ k → ratio 1 (suc k) refl) (from 0 to
> n))
> 
> 
> test : Nat → Rational
> test n = harmonic n * 0
> 
> 
> main : IO ⊤
> main =
>   caseM getArgs of λ where
>     (s ∷ []) → print (test <$> parseNat s)
>     _ → pure _
> 


1. It appears that final  fn - fn  in my test costs as much as all the 
   rest.
Instead using (_* 0), as in your test, reduces the cost twice (because
fn is large).

And I suggest to apply the final taking a remainder by 1000: 

  toSmall :  Fraction → ℕ
  toSmall f =
            rem $ divMod (∣ num f ∣ + ∣ denom f ∣) 1000 {suc≢0}

take the remainder by 1000
(my divMod is agda-prelude.divMond with swapped arguments -
the classical tradition is that the dividend is first).

This is safe for the cost, and it is safe against the rule  
_ * 0 = 0   which may be possible in some implementations.



2. Further, I have the three versions:

  _+₁_   to add fractions as in GHC (and as in agda-prelude, I expect),

  n₁/d₁ +fr n₂/d₂ =  h *fr (f' + g')
                     where
                     h = (gcd n₁ n₂) / (gcd d₁ d₂)
     -- where f' and g' are canceled by a certain gcd,
     and using coprimality for several corresponding pairs, in 
     order to reduce the usage of  gcd.

  _+fr'_  -- as in Knuth and as in DoCon.
          It finds certain additional intermediate gcd, and relies on a 
          certain additional coprimality.

In DoCon (Haskell under GHC) ,  +fr'  is the most efficient.
In DoCon-A (Agda),  the most efficient is  +₁.

+₁   now shows    5.5 sec for n = 2000,  and  288 sec for 10,000
(3 GHz machine).

+fr      shows   11.8 sec for n = 2000,  and  625 sec for 10,000.

+fr'     is only 25% faster than _+fr_
         (and it must be many times faster).


Probably,  +₁  has performance close to agda-prelude.
But I need to find of how to fix  +fr'   in order to make it close to
DoCon.
  
Regards,

------
Sergei






More information about the Agda mailing list