Рунге-Кутта (RK4) в Haskell, проблема с системой типов

Я пытаюсь реализовать Рунге-Кутта 4-го порядка в Haskell, но мне сложно использовать систему типов Haskell для этой задачи. Может кто поможет? Я хочу изменить типы State и DState на классы типов в следующем коде:

data State = State Double deriving (Show)
data DState = DState Double deriving (Show)

update :: State -> DState -> State
update (State x) (DState y) = State (x+y)

add :: DState -> DState -> DState
add (DState x) (DState y) = DState (x + y)

scale :: Double -> DState -> DState
scale h (DState x) = DState (h*x)


update_rk4 :: State -> (Double -> State -> DState) -> Double -> Double -> State
update_rk4 y f t h = update y (scale (h*(1.0/6.0)) s) where
  s = add k1 (add s2 (add s3 k4))
  s2 = scale 2 k2
  s3 = scale 2 k3
  k1 = f t y
  k2 = f (t+0.5*h) ( update y (scale (0.5*h) k1) )
  k3 = f (t+0.5*h) ( update y (scale (0.5*h) k2) )
  k4 = f (t+h) ( update y (scale h k3) )

Сложно сформулировать классы типов, поскольку State и DState взаимосвязаны в том смысле, что конкретный экземпляр State требует определенного экземпляра DState .. Или, возможно, существует какой-то другой подход, который я не вижу?


person tero    schedule 19.10.2014    source источник


Ответы (2)


Это не совсем то, для чего вы хотите использовать собственные классы типов. Haskell - это не объектно-ориентированный язык, на котором вы создаете классы для всего, скорее, классы должны отражать «глубокие математические концепции». В этом примере очень распространенной "концепцией" является то, что вы можете добавить дифференциальные изменения, и, конечно же, такие классы уже существуют .

update :: (AffineSpace st, d ~ Diff st) => st -> d -> st
      -- note that idiomatic argument order would be `d -> st -> st` instead.
update = (.+^)

add :: VectorSpace d => d -> d -> d
add = (^+^)

scale :: (VectorSpace d, h ~ Scalar d)
   => h -> d -> d
scale = (*^)


update_rk4 :: (AffineSpace st, d ~ Diff st, VectorSpace d, h ~ Scalar d, Fractional h)
     => st -> (h -> st -> d) -> h -> h -> st
     -- again, more idiomatic order is `(h -> st -> d) -> h -> h -> st -> st`.

Что касается того, почему я рекомендую ставить аргумент st последним: в Haskell часто применяют функции частично и η- уменьшите «аргумент конвейерной обработки». В этом примере вполне вероятно, что вы захотите повторно использовать конкретный "RK4-stepper", и если аргумент st является последним, вы можете сделать это просто с помощью

simStep :: ParticularState -> ParticularState
simStep = update_rk4 f t h
 where f t y = ...
       ...

Если вам уже нужно было привязать переменную y в simStep y = update_rk4 y f t h, вам нужно будет либо затенять ее с помощью объявления f, либо устранять неоднозначность с помощью неудобного f t y' = .... В данном случае это не большой выигрыш, но если вы последовательно применяете идею η-сокращения, это может значительно очистить ваш код в целом.

person leftaroundabout    schedule 19.10.2014

Вы можете использовать многопараметрические классы типов с функциональными зависимостями, хотя это не стандартный Haskell 98, но Расширения GHC. Это позволяет вам определить класс типов, методы которого не определяют все параметры типа, что в противном случае было бы неоднозначным.

Например,

class RK4 state dstate | dstate -> state where
    update :: state -> dstate -> state
    add :: dstate -> dstate -> dstate
    scale :: dstate -> dstate -> dstate

Без функциональной зависимости add и scale были бы неоднозначными, потому что их вызовы не исправляли бы тип state, делая невозможным разрешение ограничения класса типов.

См. Другие примеры, руководства и обсуждения по указанной выше ссылке. См. Также сравнение функциональных зависимостей и семейств типов, которое используется в другом отклик.

person Mark Brown    schedule 19.10.2014