【问题标题】:Numeric.AD - type variable escaping its scopeNumeric.AD - 类型变量逃脱其范围
【发布时间】:2015-08-15 17:23:36
【问题描述】:

我正在尝试在 Haskell 中使用 automatic differentiation 来解决非线性控制问题,但在让它工作时遇到了一些问题。我基本上有一个cost 函数,应该在给定初始状态的情况下对其进行优化。类型有:

data Reference a = Reference a deriving Functor
data Plant a = Plant a deriving Functor

optimize :: (RealFloat a) => Reference a -> Plant a -> [a] -> [[a]]
optimize ref plant initialInputs = gradientDescent (cost ref plant) initialInputs

cost :: (RealFloat a) => Reference a -> Plant a -> [a] -> a
cost = ...

这会导致以下错误消息:

Couldn't match expected type `Reference
                                (Numeric.AD.Internal.Reverse.Reverse s a)'
            with actual type `t'
  because type variable `s' would escape its scope
This (rigid, skolem) type variable is bound by
  a type expected by the context:
    Data.Reflection.Reifies s Numeric.AD.Internal.Reverse.Tape =>
    [Numeric.AD.Internal.Reverse.Reverse s a]
    -> Numeric.AD.Internal.Reverse.Reverse s a
  at test.hs:13:5-50
Relevant bindings include
  initialInputs :: [a] (bound at test.hs:12:20)
  ref :: t (bound at test.hs:12:10)
  optimize :: t -> t1 -> [a] -> [[a]] (bound at test.hs:12:1)
In the first argument of `cost', namely `ref'
In the first argument of `gradientDescent', namely
  `(cost ref plant)'

我什至不确定我是否正确理解了错误。是不是refplant的类型需要访问s,这在gradientDescent的第一个参数范围内?

是否有可能做到这一点?在寻找解决方案时,我尝试将问题简化为最小示例,发现以下定义会产生类似的错误消息:

optimize f inputs = gradientDescent f inputs 

这看起来很奇怪,因为optimize = gradientDescent 不会产生任何错误。

【问题讨论】:

  • ReferencePlant 是什么?
  • stackoverflow.com/a/29393417/414413相关,这两个问题都是关于gradientDescent的第一个参数的类型,答案基本相同,但提示问题的错误不同。
  • ReferencePlant 是仿函数。更具体地说,Reference 是一个简单的代数数据类型,Plant 是来自netwire 库的Wire 的类型别名。
  • optimize = gradientDescent 有效,因为未应用它可以并且将推断出 rank-n 类型。当你像这样扩展它时,必须指定 rank-n 类型,因为它超出了任何类型系统可以推断的范围。

标签: haskell automatic-differentiation


【解决方案1】:

cost ref plant 的类型为 [a] -> a,其中 aoptimize 的签名中的 a 相同

optimize :: (RealFloat a) => Reference a -> Plant a -> [a] -> [[a]]
                                       ^          ^
                                       |          ------------
                                       ------------------v   v
optimize ref plant initialInputs = gradientDescent (cost ref plant) initialInputs
                                                         ^   ^
                                   -----------------------   |
                                   v          v---------------
cost :: (RealFloat a) => Reference a -> Plant a -> [a] -> a
cost = ...

但是gradientDescent的类型是

gradientDescent :: (Traversable f, Fractional a, Ord a) =>
                   (forall s. Reifies s Tape => f (Reverse s a) -> Reverse s a) -> 
                   f a -> [f a]

gradientDescent 的第一个参数需要能够采用(对于 any s[Reverse s a] 并返回 Reverse s a,但 cost ref plant 只能采用 [a]返回一个a

由于ReferencePlant 都是Functors,您可以将refplantReference aPlant a 转换为Reference (Reverse s a)Plant (Reverse s a) @98765444 987654322@.

optimize :: (RealFloat a) => Reference a -> Plant a -> [a] -> [[a]]
optimize ref plant initialInputs = gradientDescent (cost (fmap auto ref) (fmap auto plant)) initialInputs

【讨论】:

  • 谢谢,我现在可以使用它了,虽然它很丑。出于某种原因,fmaping auto 不起作用,我必须改用 realToFrac。我还必须在两个参数上两次fmap。此外,Plant a 是来自 netwireWire s e m a a 的别名,这会使事情复杂化。如果可行,我现在将测试我的解决方案并发布答案。
  • 如果Plant a 是别名Wire s e m a a,那么Plant 就不是Functor。你在两个参数上都必须fmap 两次是什么意思?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-21
  • 2013-01-16
  • 2012-05-10
  • 2017-01-02
  • 1970-01-01
  • 2014-05-04
相关资源
最近更新 更多