turion / rhine

Haskell Functional Reactive Programming framework with type-level clocks
http://hackage.haskell.org/package/rhine
124 stars 21 forks source link

Time translation #362

Closed reubenharry closed 1 month ago

reubenharry commented 1 month ago

Is there a signal function in Rhine that performs time translation? I.e. an operator $L$ with $L(f)(t) = f(t-a)$?

In fact, I'd like something more general, of the type shiftBy :: (Monad m, Diff (Time cl) ~ a) => ClSF m cl (c, a) c? I have something similar for MSFs in Dunai, namely:

shiftBy :: Monad m => MSF m (c, Int) c
shiftBy = accumulateWith (\(x, n) xs -> take n $ x : xs) [] >>> arr last

Perhaps I should be doing a rescaling of clocks instead? E.g. RescalingS?

(The reason I want this is that I've been implementing some SDEs in Rhine, and there's a position-dependent rescaling that I want to implement. Because it is position dependent, I can't simply rescale the clock: that is, I have dt -> f(x)dt)

reubenharry commented 1 month ago

OK, I found delayBy. But this is still a fixed delay, so I'll see if I can modify it

reubenharry commented 1 month ago

This did the trick:

historySince' ::
  (Monad m, Ord (Diff (Time cl)), TimeDomain (Time cl)) =>
  -- | The size of the time window

  ClSF m cl (a, Diff (Time cl)) (Seq (TimeInfo cl, a))
historySince' = D.readerS $ accumulateWith undefined empty
  where
      appendValue :: (Ord (Diff (Time cl)), TimeDomain (Time cl)) => (TimeInfo cl, (a, Diff (Time cl))) -> Seq (TimeInfo cl, a) -> Seq (TimeInfo cl, a)
      appendValue (ti, (a, dTime)) tias = takeWhileL (recentlySince dTime ti) $ (ti, a) <| tias
      recentlySince dTime ti (ti', _) = diffTime (absolute ti) (absolute ti') < dTime

delayBy' ::
  (Monad m, Ord (Diff td), TimeDomain td) =>
  -- | The time span to delay the signal

  BehaviorF m td (a, Diff td) a
delayBy' = historySince'  >>> arr (viewr >>> safeHead) >>> lastS undefined >>> arr snd
  where
    safeHead EmptyR = Nothing
    safeHead (_ :> a) = Just a
turion commented 1 month ago

Yes, delayBy would have been my first recommendation. You can probably modify it to have a variable delay.

But note that this implicitly assumes that you can interpolate your signal with a zero order hold, i.e. keeping the newest value before the desired time. Maybe this is a good assumption in your situation. In general, you'd want to resample in some way. I'm not sure how you'd do that with a variable delay. For a fixed delay it might be conceivable to have a "delayed clock", but for a variable delay this sounds complicated (possibly a clock in StateT).

turion commented 1 month ago

This sounds like a useful addition, thanks :) I would merge a PR containing it.