haskell AD 模块 : difficulties with Mode typeclass

标签 haskell

我是 Haskell 的新手,很难理解如何让我的程序使用自动微分模块 AD 进行类型检查。

我的目标是实现一个隐式 Euler ODE 求解器,它利用牛顿法对离散方程进行数值求解。 (我试图将其推广到 ODE 系统,但对于我的问题,仅考虑一个 ODE 的情况就足够了)

我已经实现了牛顿方法如下:

import Numeric.AD
import Numeric.AD.Rank1.Forward (Forward, diff')

newton :: (Fractional a, Eq a, Ord a) => (Forward a -> Forward a) -> a -> a -> a
newton equation guess tolerance
    | abs (equation (auto guess)) < auto tolerance = guess
    | otherwise = newton equation newGuess tolerance
    where
        newGuess = guess - correction
        (y,y') = diff' equation guess
        correction = y/y'

这个功能有效,在某种意义上我可以使用它
mySqrtOfTwo = newton (\(x) -> x^2 - 2) 1 0.001

但是,如果我尝试在另一个功能中使用它,例如
impEuler f (x, y) newx = (newx, newy) 
    where
        newy = newton fDisc y 1e-3
        fDisc yUnknown = yUnknown - y + (newx - x) * (f (x,yUnknown))

我得到错误
• Occurs check: cannot construct the infinite type: b ~ Forward b
• In the second argument of ‘newton’, namely ‘y’
      In the expression: newton fDisc y 1e-3
      In an equation for ‘newy’: newy = newton fDisc y 1e-3

我想我明白为什么会出现这个错误,但我不明白为什么它只发生在 newton函数在另一个函数中使用,而不是在直接调用时使用。此外,我想知道处理这个问题的正确方法是什么。

我有想过实现功能newton稍有不同,以这样一种方式,它具有类型
newton :: (Fractional a, Eq a, Ord a) => (a -> a) -> a -> a -> a

但我不知道如何做到这一点,如果这甚至是好的风格。

为了简化问题:我知道我可以使用 autoa 出发至Forward a ,但我不知道如何走另一条路,如果这甚至可能。

编辑:正如@leftroundabout 建议的那样,我实现了如下功能:
impEuler :: (Double -> Forward Double -> Forward Double) -> (Double,Double) -> Double -> Double
impEuler f (x, y) newx = newy
where newy = newton fDisc y 1e-3
      fDisc :: Forward Double -> Forward Double
      fDisc yUnknown = yUnknown - realToFrac y - realToFrac (newx - x) * f x yUnknown

这要求传递给 impEuler 的 ode 也具有类型 (Double -> Forward Double -> Forward Double) ,我想避免这种情况,因为我可能决定使用显式方法(不需要牛顿法)来解决 ode。因此,我添加了功能
odePromoter :: (Double -> Double -> Double) -> (Double -> Forward Double -> Forward Double)
odePromoter ode x y = realToFrac (ode x (realToFrac y))

为了将类型 (Double -> Double -> Double) 的 ode 转换为类型 (Double -> Forward Double -> Forward Double) 之一。

最佳答案

关键是,fDisc必须能够支持自动微分。 IE。它的类型必须像 Forward Double -> Forward Double .然而,在

   fDisc yUnknown = yUnknown - y + (newx - x) * (f (x,yUnknown))

你有值y , xnewx , 这些是简单的具体数字,可能是 Double . Haskell 从不隐式转换/提升类型,因此您需要 fDisc :: Double -> Double , 这意味着 newton无法使用它。

解决方案:允许显式提升这些值。一个标准的方法是 realToFrac .
impEuler :: (Double -> Forward Double -> Forward Double) -> (Double,Double) -> Double
impEuler f (x, y) newx = newy
 where newy = newton fDisc y 1e-3
       fDisc :: Forward Double -> Forward Double
       fDisc yUnknown = yUnknown - realToFrac y + realToFrac (newx - x) * f x yUnknown

请注意,我必须对函数 f 进行柯里化(Currying)。 ,因此它在解变量之前将时间参数单独作为常数。

关于haskell AD 模块 : difficulties with Mode typeclass,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51560542/

相关文章:

haskell - 如何测试这种数据类型的半群定律?

haskell - 如果 Haskell 纯粹是功能性的,为什么它可以在 Haskell 中休眠/暂停执行?

haskell - 是否可以将 cmake 用于 Haskell 项目?

haskell - 语法 $$ 在 Haskell 中是什么意思?

haskell - 使用 Parsec 从文本文件中选取数据

matlab - 如何在 Haskell 中添加两个列表,如 matlab?

haskell - 使用返回子列表的函数在 haskell 中创建无限递归列表

haskell - 请解释类型同义词

arrays - Haskell 中的大小索引可变数组

haskell - 使用导入文件中定义的数据构造函数时出现问题