mealy 0.0.1 → 0.0.2
raw patch · 3 files changed
+93/−73 lines, 3 filesdep +matrixdep ~numhaskdep ~numhask-arrayPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: matrix
Dependency ranges changed: numhask, numhask-array
API changes (from Hackage documentation)
- Data.Mealy: instance (NumHask.Algebra.Abstract.Additive.Additive a, NumHask.Algebra.Abstract.Additive.Additive b) => GHC.Base.Monoid (Data.Mealy.Averager a b)
- Data.Mealy: instance (NumHask.Algebra.Abstract.Additive.Additive a, NumHask.Algebra.Abstract.Additive.Additive b) => GHC.Base.Semigroup (Data.Mealy.Averager a b)
+ Data.Mealy: instance (NumHask.Algebra.Additive.Additive a, NumHask.Algebra.Additive.Additive b) => GHC.Base.Monoid (Data.Mealy.Averager a b)
+ Data.Mealy: instance (NumHask.Algebra.Additive.Additive a, NumHask.Algebra.Additive.Additive b) => GHC.Base.Semigroup (Data.Mealy.Averager a b)
+ Data.Mealy: instance GHC.Exception.Type.Exception Data.Mealy.MatrixException
+ Data.Mealy: instance GHC.Show.Show Data.Mealy.MatrixException
- Data.Mealy: absma :: (Divisive a, Additive a, Signed a) => a -> Mealy a a
+ Data.Mealy: absma :: (Divisive a, Signed a) => a -> Mealy a a
- Data.Mealy: alpha :: (Field a, ExpField a, KnownNat n) => a -> Mealy (Array '[n] a, a) a
+ Data.Mealy: alpha :: (ExpField a, KnownNat n, Fractional a, Eq a) => a -> Mealy (Array '[n] a, a) a
- Data.Mealy: beta :: (Field a, Field a, KnownNat n) => a -> Mealy (Array '[n] a, a) (Array '[n] a)
+ Data.Mealy: beta :: (Field a, KnownNat n, Fractional a, Eq a) => a -> Mealy (Array '[n] a, a) (Array '[n] a)
- Data.Mealy: reg :: (Field a, ExpField a, KnownNat n) => a -> Mealy (Array '[n] a, a) (Array '[n] a, a)
+ Data.Mealy: reg :: (ExpField a, KnownNat n, Fractional a, Eq a) => a -> Mealy (Array '[n] a, a) (Array '[n] a, a)
Files
- mealy.cabal +9/−4
- src/Data/Mealy.hs +77/−62
- src/Data/Mealy/Quantiles.hs +7/−7
mealy.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: mealy-version: 0.0.1+version: 0.0.2 synopsis: See readme.md description: See readme.md for description. category: project@@ -27,10 +27,11 @@ generic-lens >= 2.0, hmatrix >= 0.20, lens,+ matrix >= 0.3.6 && < 0.3.7, mwc-probability, mwc-random,- numhask >= 0.6 && < 0.7,- numhask-array >= 0.7 && < 0.8,+ numhask >= 0.7 && < 0.8,+ numhask-array >= 0.8 && < 0.9, primitive >= 0.7, profunctors >= 5.5, tdigest,@@ -54,6 +55,8 @@ -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints+ -fwrite-ide-info+ -hiedir=.hie test-suite test type: exitcode-stdio-1.0@@ -63,7 +66,7 @@ build-depends: base >=4.7 && <5, doctest,- numhask >= 0.6 && < 0.7,+ numhask >= 0.7 && < 0.8, mealy default-language: Haskell2010 default-extensions:@@ -77,3 +80,5 @@ -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints+ -fwrite-ide-info+ -hiedir=.hie
src/Data/Mealy.hs view
@@ -13,14 +13,15 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeOperators #-} {-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -Wno-incomplete-patterns #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-type-defaults #-}-{-# OPTIONS_GHC -Wno-incomplete-patterns #-} -- | Online statistics for ordered data (such as time-series data), modelled as [mealy machines](https://en.wikipedia.org/wiki/Mealy_machine) module Data.Mealy@@ -72,18 +73,21 @@ ) where -import Control.Lens hiding ((:>), Empty, Unwrapped, Wrapped, index, (|>))+import Control.Lens hiding (Empty, Unwrapped, Wrapped, index, (:>), (|>)) import Data.Fold hiding (M) import Data.Functor.Rep import Data.Generics.Labels ()+-- import qualified NumHask.Array.HMatrix as HM++-- import qualified Numeric.LinearAlgebra as LA++import qualified Data.Matrix as M import qualified Data.Sequence as Seq import qualified NumHask.Array.Fixed as F-import qualified NumHask.Array.HMatrix as HM import NumHask.Array.Shape (HasShape) import NumHask.Prelude hiding (L1, State, StateT, asum, fold, get, replace, runState, runStateT, state)-import qualified Numeric.Backprop as B import Numeric.Backprop (BVar, Reifies, W)-import qualified Numeric.LinearAlgebra as LA+import qualified Numeric.Backprop as B import qualified Prelude.Backprop as PB import qualified Prelude as P @@ -104,23 +108,21 @@ -- >>> xs2 <- rvs g 10000 -- >>> xsp <- rvsp g 10000 0.8 -{- | A 'Mealy' is a triple of functions-- * (a -> b) __inject__ Convert an input into the state type.- * (b -> a -> b) __step__ Update state given prior state and (new) input.- * (c -> b) __extract__ Convert state to the output type.-- By adopting this order, a Mealy sum looks like:--> M id (+) id--where the first id is the initial injection to a contravariant position, and the second id is the covriant extraction.-- __inject__ kicks off state on the initial element of the Foldable, but is otherwise be independent of __step__.--> scan (M e s i) (x : xs) = e <$> scanl' s (i x) xs---}+-- | A 'Mealy' is a triple of functions+--+-- * (a -> b) __inject__ Convert an input into the state type.+-- * (b -> a -> b) __step__ Update state given prior state and (new) input.+-- * (c -> b) __extract__ Convert state to the output type.+--+-- By adopting this order, a Mealy sum looks like:+--+-- > M id (+) id+--+-- where the first id is the initial injection to a contravariant position, and the second id is the covriant extraction.+--+-- __inject__ kicks off state on the initial element of the Foldable, but is otherwise be independent of __step__.+--+-- > scan (M e s i) (x : xs) = e <$> scanl' s (i x) xs newtype Mealy a b = Mealy {l1 :: L1 a b} deriving (Profunctor, Category) via L1 deriving (Functor, Applicative) via L1 a@@ -148,10 +150,9 @@ scan (M i s e) (x : xs) = fromList (e <$> scanl' s (i x) xs) -- | Most common statistics are averages, which are some sort of aggregation of values (sum) and some sort of sample size (count).-newtype Averager a b- = Averager- { sumCount :: (a, b)- }+newtype Averager a b = Averager+ { sumCount :: (a, b)+ } deriving (Eq, Show) -- | Pattern for an 'Averager'.@@ -216,7 +217,7 @@ -- -- >>> fold (absma 1) xs0 -- 0.7894201075535578-absma :: (Divisive a, Additive a, Signed a) => a -> Mealy a a+absma :: (Divisive a, Signed a) => a -> Mealy a a absma r = online abs (* r) {-# INLINEABLE absma #-} @@ -330,13 +331,12 @@ reg1 :: (ExpField a) => Mealy a a -> Mealy (a, a) (a, a) reg1 m = (,) <$> alpha1 m <*> beta1 m -data RegressionState (n :: Nat) a- = RegressionState- { _xx :: F.Array '[n, n] a,- _x :: F.Array '[n] a,- _xy :: F.Array '[n] a,- _y :: a- }+data RegressionState (n :: Nat) a = RegressionState+ { _xx :: F.Array '[n, n] a,+ _x :: F.Array '[n] a,+ _xy :: F.Array '[n] a,+ _y :: a+ } deriving (Functor) -- | multiple regression@@ -357,22 +357,37 @@ -- > let zs = zip (zipWith (\x y -> fromList [x,y] :: F.Array '[2] Double) xs1 xs2) ys -- > fold (beta 0.99) zs -- [0.4982692361226971, 1.038192474255091]-beta :: (Field a, LA.Field a, KnownNat n) => a -> Mealy (F.Array '[n] a, a) (F.Array '[n] a)+beta :: (Field a, KnownNat n, Fractional a, Eq a) => a -> Mealy (F.Array '[n] a, a) (F.Array '[n] a) beta r = M inject step extract where+ -- extract :: Averager (RegressionState n a) a -> (F.Array '[n] a) extract (A (RegressionState xx x xy y) c) =- liftHM2- (\a b -> LA.pinv a LA.<> LA.tr b)- ((one / c) *. (xx - F.expand (*) x x))- ((xy - (y *. x)) .* (one / c))+ (\a b -> inverse a `F.mult` b)+ ((one / c) .* (xx - F.expand (*) x x))+ ((xy - (y .* x)) *. (one / c)) step x (xs, y) = rsOnline r x (inject (xs, y))+ -- inject :: (F.Array '[n] a, a) -> Averager (RegressionState n a) a inject (xs, y) =- A (RegressionState (F.expand (*) xs xs) xs (y *. xs) y) one+ A (RegressionState (F.expand (*) xs xs) xs (y .* xs) y) one {-# INLINEABLE beta #-} -liftHM2 :: (LA.Field a, HasShape s, HasShape s', HasShape s'') => (LA.Matrix a -> LA.Matrix a -> LA.Matrix a) -> F.Array s a -> F.Array s' a -> F.Array s'' a-liftHM2 f a b = HM.toFixed $ HM.Array $ f (HM.unArray . HM.fromFixed $ a) (HM.unArray . HM.fromFixed $ b)+toMatrix :: (KnownNat n, KnownNat m) => F.Array [m, n] a -> M.Matrix a+toMatrix a = M.matrix m n (index a . (\(i, j) -> [i, j]))+ where+ (m : n : _) = F.shape a +fromMatrix :: (KnownNat n, KnownNat m) => M.Matrix a -> F.Array [m, n] a+fromMatrix = fromList . M.toList++data MatrixException = MatrixException+ deriving (Show)++instance Exception MatrixException++-- | The inverse of a square matrix.+inverse :: (KnownNat n, Fractional a, Eq a) => F.Array [n, n] a -> F.Array [n, n] a+inverse = either (const $ throw MatrixException) fromMatrix . M.inverse . toMatrix+ rsOnline :: (Field a, KnownNat n) => a -> Averager (RegressionState n a) a -> Averager (RegressionState n a) a -> Averager (RegressionState n a) a rsOnline r (A (RegressionState xx x xy y) c) (A (RegressionState xx' x' xy' y') c') = A (RegressionState (liftR2 d xx xx') (liftR2 d x x') (liftR2 d xy xy') (d y y')) (d c c')@@ -380,7 +395,7 @@ d s s' = r * s + s' -- | alpha in a multiple regression-alpha :: (LA.Field a, ExpField a, KnownNat n) => a -> Mealy (F.Array '[n] a, a) a+alpha :: (ExpField a, KnownNat n, Fractional a, Eq a) => a -> Mealy (F.Array '[n] a, a) a alpha r = (\xs b y -> y - sum (liftR2 (*) b xs)) <$> lmap fst (arrayify $ ma r) <*> beta r <*> lmap snd (ma r) {-# INLINEABLE alpha #-} @@ -397,7 +412,7 @@ -- > let zs = zip (zipWith (\x y -> fromList [x,y] :: F.Array '[2] Double) xs1 xs2) ys -- > fold (reg 0.99) zs -- ([0.4982692361226971, 1.038192474255091],2.087160803386695e-3)-reg :: (LA.Field a, ExpField a, KnownNat n) => a -> Mealy (F.Array '[n] a, a) (F.Array '[n] a, a)+reg :: (ExpField a, KnownNat n, Fractional a, Eq a) => a -> Mealy (F.Array '[n] a, a) (F.Array '[n] a, a) reg r = (,) <$> beta r <*> alpha r {-# INLINEABLE reg #-} @@ -497,15 +512,14 @@ -- s_{t+1} & = (alpha_t^s + beta_t^{x->s} * ma_t^x + beta_t^{s->s} * std_t^x) * N(0,1) -- \end{align} -- \]-data Model1- = Model1- { alphaX :: Double,- alphaS :: Double,- betaMa2X :: Double,- betaMa2S :: Double,- betaStd2X :: Double,- betaStd2S :: Double- }+data Model1 = Model1+ { alphaX :: Double,+ alphaS :: Double,+ betaMa2X :: Double,+ betaMa2S :: Double,+ betaStd2X :: Double,+ betaStd2S :: Double+ } deriving (Eq, Show, Generic) zeroModel1 :: Model1@@ -528,8 +542,10 @@ + (m1 ^. #betaStd2S) * (s - 1) ) + m1 ^. #alphaX- + (m1 ^. #betaMa2X) * m- + (m1 ^. #betaStd2X) * (s - 1)+ + (m1 ^. #betaMa2X)+ * m+ + (m1 ^. #betaStd2X)+ * (s - 1) foldB :: (Reifies s W) => (BVar s Double -> BVar s Double) -> BVar s Double -> BVar s [Double] -> BVar s Double foldB f r xs = divide (PB.foldl' (step' f r) (B.T2 0 0) xs)@@ -542,12 +558,11 @@ -- | A rough Median. -- The average absolute value of the stat is used to callibrate estimate drift towards the median-data Medianer a b- = Medianer- { medAbsSum :: a,- medCount :: b,- medianEst :: a- }+data Medianer a b = Medianer+ { medAbsSum :: a,+ medCount :: b,+ medianEst :: a+ } -- | onlineL1' takes a function and turns it into a `Mealy` where the step is an incremental update of an (isomorphic) median statistic. onlineL1' ::
src/Data/Mealy/Quantiles.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE RebindableSyntax #-}+{-# LANGUAGE StrictData #-} module Data.Mealy.Quantiles ( median,@@ -17,12 +18,11 @@ import qualified Data.Vector.Unboxed as VU import NumHask.Prelude hiding (fold) -data OnlineTDigest- = OnlineTDigest- { td :: TDigest 25,- tdN :: Int,- tdRate :: Double- }+data OnlineTDigest = OnlineTDigest+ { td :: TDigest 25,+ tdN :: Int,+ tdRate :: Double+ } deriving (Show) emptyOnlineTDigest :: Double -> OnlineTDigest