Monoid用于补偿(Kahan)求和?

时间:2017-04-12 13:30:46

标签: haskell floating-point sum monoids

假设我有这样的代码,用于执行Double s的补偿(Kahan-Babuška-Neumaier)求和:

-- A type for compensated summation.
data Sum = Sum {-# UNPACK #-} !Double {-# UNPACK #-} !Double
  deriving (Eq, Ord, Show)

-- Add a number to the Sum, updating the error value as well.
addSum :: Sum -> Double -> Sum
addSum (Sum s c) x = Sum s' c' where
  s' = s + x
  d  | abs s >= abs x = (s - s') + x
     | otherwise      = (x - s') + s
  c' = c + d
{-# INLINE addSum #-}

-- Get the compensated value from the Sum.
getSum :: Sum -> Double
getSum (Sum s c) = s + c
{-# INLINE getSum #-}

-- Add a whole list (or list-like object) of Doubles.
sumAll :: Foldable f => f Double -> Double
sumAll ls = getSum $ foldl' addSum (Sum 0 0) ls
{-# INLINE sumAll #-}

但是,我还需要一个Monoid,因为我想并行计算值。我正在寻找的是:

-- Make a Sum from a Double value. It has an error of 0.
mkSum :: Double -> Sum
mkSum x = Sum x 0
{-# INLINE mkSum #-}

instance Monoid Sum where
  mempty = Sum 0 0
  {-# INLINE mempty #-}

  mappend (Sum sa ca) (Sum sb cb) = ...?

我认为它可能有类似的形式:

  mappend a@(Sum sa ca) b@(Sum sb cb)
    | ???       = addSum a (sb + cb)
    | otherwise = addSum b (sa + ca)

我希望它是可交换的,并且至少与正常的Double添加相关联,如果不是更多的话。

1 个答案:

答案 0 :(得分:0)

如果有正确的mappend可能

mappend :: Sum -> Sum -> Sum
mappend (Sum s c) (Sum x c2) = Sum s' c' where
  s' = s + x
  d  | abs s >= abs x = (s - s') + x
     | otherwise      = (x - s') + s
  c' = (c + c2) + d

来源:预感。

预感是满足财产mappend s1 (Sum x 0) = addSum s1 x并且可交换的最简单的事情。它的相关性很难检查。

检查关联性很困难,因为添加Double s并不是关联的。因此mappend的{​​{1}}不是关联的;问题是,是否足够关联 。一个可能的标准是,关联Sum时的错误变化小于或等于将参数转换为mappend时的错误变化。

对于您的标准,它已经"至少与正常sumAll添加"相关联,因为它是正常的Double添加。事实上,它是对正常Double添加的改进。如果Double是关联的,则Double将为d。添加到总和0的任何非零d都是改进关于c1 + c2添加的关联性,跟踪和纠正非关联性中的错误Double s。

使用更好的求和Double的方法可以改善c'的计算。他们可以分类,la:

Double

或使用Kahan求和自己总结sumThree :: Double -> Double -> Double -> Double sumThree a b c | abs a >= abs b && abs a >= abs c = (b + c) + a | abs b >= abs a && abs b >= abs c = (a + c) + b | otherwise = (a + b) + c 。如果他们自己使用Kahan求和进行求和,sumAll [c, c2, d]无法按sumAll实现。