简体   繁体   English

在状态monad上实现递归关系(在Haskell或Scala中)

[英]Implementing recurrence relations on State monads (in Haskell or Scala)

I am working on a new implementation of the operators in http://www.thalesians.com/archive/public/academic/finance/papers/Zumbach_2000.pdf EDIT: clearer explanation here: https://www.olseninvest.com/customer/pdf/paper/001207-emaOfEma.pdf 我正在http://www.thalesians.com/archive/public/academic/finance/papers/Zumbach_2000.pdf编写运营商的新实现。编辑:更清楚的解释: https//www.olseninvest.com/客户/ PDF /纸/ 001207-emaOfEma.pdf

Briefly, it's a whole bunch of cool time series operators based on the recurrence relation of the exponential moving average, where each application of the ema() operator takes the new value and the previous result of the ema. 简而言之,它是基于指数移动平均值的递归关系的一大堆很酷的时间序列运算符,其中ema()运算符的每个应用程序都获取新值和ema的先前结果。 I can't seem to do latex on this stack exchange, but anyway my problem now is a software problem. 我似乎无法在这个堆栈交换上做乳胶,但无论如何我的问题现在是一个软件问题。

I implemented this in Scala by hiding a var deep inside the thunks that create EMA functions. 我在Scala中通过在创建EMA函数的thunk中隐藏var深入实现了它。 This all works, but it's super tricky, because calling ema(5) and then ema(5) again will naturally lead to a different result. 这一切都有效,但这是非常棘手的,因为再次调用ema(5)然后再调用ema(5)自然会导致不同的结果。 I'd like to try redoing all of this using State Monads, but I'm quickly getting lost in the weeds. 我想尝试使用State Monads重做所有这些,但我很快就迷失在杂草中。

For example, I have the following simplified EMA State monad in Haskell: 例如,我在Haskell中有以下简化的EMA State monad:

import Control.Monad.State

type EMAState = Double
type Tau = Double

ema :: Tau -> Double -> State EMAState Double
ema tau x = state $ \y ->
  let alpha = 1 / tau
      mu = exp(-alpha)
      mu' = 1 - mu
      y' = (mu * y) + (mu' * x)
  in (y', y')

which I can readily test in GHCI: 我可以在GHCI中轻松测试:

*Main Control.Monad.State> runState (ema 5 10) 0
(1.8126924692201818,1.8126924692201818)

applying the input 10 to a 5-period EMA initialized to 0. This is all well and good, using forM I can apply multiple input values etc. Now, the next step is to implement an "iterated EMA", which is an EMA applied to itself N times. 将输入10应用于初始化为0的5周期EMA。这一切都很好,使用forM我可以应用多个输入值等。现在,下一步是实现“迭代EMA”,这是一个EMA应用对自己N次。

iEMA[n](x) = EMA(iEMA[n-1](x))

Each of these intermediate EMAs will need to have their own state, aka previous result, to correctly calculate the vector of iterated EMAs. 这些中间EMA中的每一个都需要具有它们自己的状态,也就是先前的结果,以正确地计算迭代的EMA的矢量。 So, what I am looking for, is something which like this, (I think): 所以,我正在寻找的东西是这样的,(我认为):

iema :: Int -> Tau -> Double -> State [EMAState] [Double]

Which is essentially a daisy chain of EMAs: 这本质上是一个菊花链EMA:

iEMA[3](x) = EMA(EMA(EMA(x,s1),s2),s3) = (x, [s1,s2,s3]) -> ([y1,y2,y3], [s1',s2',s3'])

And if all I care about is the 3rd iterated EMA ... 如果我关心的是第3次迭代的EMA ......

... -> (y3, [s1', s2', s3'])

The paper moves on from there, creating ever more complex operators built on iterated EMAs and averages of them etc, so I want to be able to functionally and purely compose these stateful operators building ever more complex states, but still quite simple input and output. 本文继续从那里开始,创建了基于迭代EMA和平均值的更复杂的运算符等,所以我希望能够在功能上和纯粹地组合这些有状态的运算符来构建更复杂的状态,但仍然是非常简单的输入和输出。

I really feel like this is what functional programming is good at, but I don't yet have the expertise to see how to put together these State monads in the correct way. 我真的觉得这就是函数式编程擅长的东西,但我还没有专业知识来看看如何以正确的方式组合这些状态monad。 Could someone please point me in the right direction with these iterated recurrence operators? 有人可以用这些迭代递归算子指出我正确的方向吗?

EDIT: 编辑:

A couple of helpful folks have suggested repeated application of the same ema operator to the input data, but this is not sufficient. 一些有用的人建议在输入数据上重复应用相同的ema运算符,但这还不够。 Each ema operator needs to maintain it's own previous value. 每个ema运营商都需要维护自己以前的值。 Here's an example: 这是一个例子:

tau 5               
mu  0.818730753             
muprime 0.181269247             
        ema1    ema2    ema3     
    x   0       0       0       <- States_0
    1   0.1812  0.03285 0.00595 <- States_1
    5   1.0547  0.21809 0.04441 <- States_2

The x column is the raw input, ema1 uses its left for input and it's up for recurrence/state. x列是原始输入,ema1使用其左侧输入,并且它用于重复/状态。 ema2 uses its left for input (not x!) and it's up for state. ema2使用它的左边输入(不是x!),它是状态。 It's an ema (ema (x) ). 这是一封电子邮件(ema(x))。 Ditto ema3 = ema (ema (ema (x) ) ). 同上ema3 = ema(ema(ema(x)))。 What I would like to do, which I think must be possible, is given an ema state monad, compose the ema3 state monad, or even better, the [ema] state monad with each each subsequent ema operating on the output of the previous. 我想做的事情,我认为必须是可能的,给予一个电子邮件状态monad,编写ema3状态monad,甚至更好,[ema]状态monad,每个后续的ema操作在前一个输出上。

Let's build the handy old Mealy machine 让我们构建一个方便的旧Mealy机器

data Mealy i o where
  Mealy :: (i -> s -> (i, s)) -> s -> Mealy i o

which has all kinds of instances 它有各种各样的例子

instance Arrow Mealy
instance ArrowChoice Mealy
instance ArrowApply Mealy
instance Strong Mealy
instance Choice Mealy
instance Profunctor Mealy
instance Category * Mealy
instance Monad (Mealy a)
instance Functor (Mealy a)
instance Applicative (Mealy a)
instance Pointed (Mealy a)

We can use it to build recurrence relations 我们可以用它来建立递归关系

recur :: (a -> a -> a) -> a -> Mealy a a
recur f a0 = Mealy (\inp prior -> let post = f inp prior in (post, post)) a0

we can iterate them with our Category instance 我们可以用Category实例迭代它们

iter :: Int -> Mealy a a -> Mealy a a
iter 0 _ = id
iter 1 m = m
iter n m = m >>> iter (n-1) m

and then, with all this machinery, we can create an infinite stream of iterated Mealy machines 然后,通过所有这些机器,我们可以创建一个无限的迭代Mealy机器流

data Stream a = Stream a (Stream a) deriving Functor

instance Functor Stream
instance Applicative Stream
instance Foldable Stream
instance Traversable Stream

ints :: Stream Int
ints = go 0 where go n = Stream n (go $ n + 1)

jet :: Mealy a a -> Stream (Mealy a a)
jet m = fmap (`iter` m) ints

All of these together give us, essentially, your desired structure. 所有这些一起给了我们基本上你想要的结构。 But it's a little difficult to interact with directly. 但直接互动有点困难。 We'll give it its own instances to help 我们将给它自己的实例来帮助

newtype MealyJet i o = MealyJet { runMealyJet :: Stream (Mealy i o) }

instance Profunctor MealyJet
instance Applicative (MealyJet i)

instance Category MealyJet where
  id = MealyJet (pure id) -- technically this should be `jet id`, but it's equal to pure
  MealyJet f . MealyJet g = MealyJet (liftA2 (.) f g)

viewMealyJet :: MealyJet i o -> Mealy i (Stream o)
viewMealyJet (MealyJet m) = sequenceA m

And now, we can write these EMAs as needed 现在,我们可以根据需要编写这些EMA

type Tau = Double

ema :: Tau -> Mealy Double Double
ema tau = recur $ \fresh prior -> 
  let alpha = 1 / tau
      mu    = exp (negate alpha)
      mu'   = 1 - mu
   in (mu * y) + (mu' * x)

emaJet :: Tau -> MealyJet Double Double
emaJet = MealyJet . jet . ema

emaComp :: MealyJet Double Double
emaComp = emaJet 1 >>> emaJet 2 >>> emaJet 3 >>> emaJet 4 >>> emaJet 5

fiveStack :: Mealy Double (Stream Double)
fiveStack = viewMealyJet emaComp

I may not be fully understanding your use case, but possibly you are looking for something like this: 我可能无法完全理解您的用例,但可能您正在寻找以下内容:

ema' _ [] = get >>= return
ema' tau (x:xs) = do
  y <- get
  let alpha = 1 / tau
      mu = exp $ negate alpha
      mu' = 1 - mu
      y' = (mu * y) + (mu' * x)
  put y'
  ema' tau xs

It is like your original function except it accepts a list of x values, and it recursively executes for each one, updating y each time. 它就像你的原始函数,除了它接受一个x值列表,并递归执行每个值,每次更新y When none are left, it returns the value of y as the answer. 如果没有,则返回y的值作为答案。

It can be run like so: 它可以像这样运行:

*Main> evalState (ema' 5 [10]) 0
1.8126924692201818
*Main> evalState (ema' 5 [10, 10]) 0
3.2967995396436076
*Main> evalState (ema' 5 [10, 10, 10]) 0
4.511883639059737

When using the State monad, you don't need to wrap your functions in the state $ \\y -> ... business. 使用State monad时,您不需要将函数包装在state $ \\y -> ... business状态。 You can simply enclose your monadic code in a do block and use put and get to access the state. 您可以简单地将您的monadic代码包含在do块中,并使用putget来访问该状态。 In this case, for each recursive execution of the function, I grab the last y with get , and then use put after doing math to update the state. 在这种情况下,对于函数的每次递归执行,我用get最后一个y ,然后在做数学后使用put来更新状态。

I think that in your version, you are including the State monad without actually getting anything for it (since you don't use put or get ). 我认为在你的版本中,你包括State monad而没有真正获得任何东西(因为你没有使用putget )。

Also, the State monad may be overkill for this; 此外, State单子可能对此有些过分; you could accomplish the same thing using a fold over a list of x values. 你可以使用x值列表上的折叠来完成同样的事情。

Update based on the comments... 根据评论更新......

Three iterations of ema can be written using the monadic bind operator >>= like this: 可以使用monadic绑定运算符>>=这样编写三次ema迭代:

ema3 tau x = ema tau x >>= ema tau >>= ema tau

or using the Kleisli arrow: 或使用Kleisli箭头:

ema3 tau = ema tau >=> ema tau >=> ema tau

As a diagram the computation flows like this: 作为图表,计算流程如下:

          y1          /---------\
           |          |         |
           v          |         v
  x  -->  EMA   -->  EMA  -->  EMA  -->  x' = y3'
          tau        tau       tau 
           |          ^         |
           |          |         v
           \----------/         y3'

(Original answer) (原始答案)

This is not a complete answer, but perhaps the OP comment on whether this is going in the right direction. 这不是一个完整的答案,但也许OP评论这是否正朝着正确的方向发展。

Here is what I understand the computation looks like: 这是我理解的计算结果:

          y1         y2        y3
           |          |         |
           v          v         v
  x  -->  EMA   -->  EMA  -->  EMA  -->  x'
          tau1       tau2      tau3
           |          |         |
           v          v         v
          y1'        y2'       y3'

The question is whether there is an elegant way to express this as a composition of EMA blocks, eg something like: 问题是,是否有一种优雅的方式将其表达为EMA块的组合,例如:

ema tau1 >o> ema tau2 >o> ema tau3

for some operator >o> . 对于某些运算符>o>

Updated answer... 更新的答案......

Define: 限定:

combine :: [ a -> State s a ] -> a -> State [s] a
combine fs a = state $ \ys ->
  let zs = zipWith (\f y a -> runState (f a) y) fs ys
      pairs = chain a zs
      as' = map fst pairs
      a' = last as'         -- we are only returning one result in this case
      ys' = map snd pairs
  in (a', ys')

chain :: a -> [ a -> (a,s) ] -> [ (a,s) ]
chain a [] = []
chain a (f:fs) = let (a',s) = f a
                 in (a',s) : chain a' fs

ema3 t = combine $ replicate 3 (ema t)

ghci> runState (ema3 5 1) [0,0,0]
(5.956242778945897e-3,[0.18126924692201818,3.2858539879675595e-2,5.956242778945897e-3])

ghci> runState (do ema3 5 1; ema3 5 5) [0,0,0]
(4.441089130249448e-2,[1.0547569416524334,0.21809729359983737,4.441089130249448e-2])

The combine is easily modified to return all of the results - just return as' instead of a' . combine很容易修改以返回所有结果 - 只返回as'而不是a'

Original answer: 原始答案:

 combine :: (a -> State s b) -> (b -> State t c) -> (a -> State (s,t) c)
 combine f g a = state $ \(s,t) ->
   let (b,s') = runState (f a) s
       (c,t') = runState (g b) t
   in (c,(s',t'))

Then: 然后:

ema3 tau = ema tau `combine` ema tau `combine` ema tau

and em3 has type: em3有类型:

ema3 :: Tau -> Double -> State ((EMAState, EMAState), EMAState) Double

For instance: 例如:

ghci> runState (ema3 5 1) ((0,0),0)
(5.956242778945897e-3,((0.18126924692201818,3.2858539879675595e-2),5.956242778945897e-3))

Note that the state type of ema3 is ((Double,Double),Double) and not a 3-tuple or list. 请注意, ema3的状态类型是((Double,Double),Double)而不是3元组或列表。

In your example you run (ema3 5) first with input x = 1 and then with input x = 5 with initial state ((0,0),0) : 在您的示例中,首先运行(ema3 5) ,输入x = 1 ,然后输入x = 5 ,初始状态((0,0),0)

ghci> runState (do ema3 5 1; ema3 5 5) ((0,0),0)
(4.441089130249448e-2,((1.0547569416524334,0.21809729359983737),4.441089130249448e-2))

and that gives you the second row in the table. 这会给你表中的第二行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM