-- SPDX-License-Identifier: MPL-2.0

{- | Explicit-lag stationarity statistics, bounded change-point/warping
reference algorithms, and forecast diagnostics. Statistical critical values
are model-dependent approximations, not guarantees of stationarity.
-}
module HMath.TimeSeries.Diagnostics (
    Deterministic (..),
    Stationarity (..),
    augmentedDickeyFuller,
    kpss,
    changePoints,
    cusum,
    anomalies,
    DTWResult (..),
    dynamicTimeWarping,
    spectrum,
    ForecastMetric (..),
    forecastError,
) where

import Control.Monad (foldM)
import Data.Array (Array, array, (!))
import Data.Complex (Complex ((:+)), magnitude)
import Data.List (minimumBy, transpose)
import Data.Ord (comparing)
import HMath.Error
import qualified HMath.Fourier as F
import qualified HMath.Matrix as M
import qualified HMath.Matrix.LU as LU
import qualified HMath.Matrix.QR as QR
import qualified HMath.Statistics.Advanced as Stats
import HMath.TimeSeries.Core

-- | Stationarity-test deterministic terms: level-only or linear-trend specification.
data Deterministic = Level | LinearTrend deriving (Deterministic -> Deterministic -> Bool
(Deterministic -> Deterministic -> Bool)
-> (Deterministic -> Deterministic -> Bool) -> Eq Deterministic
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Deterministic -> Deterministic -> Bool
== :: Deterministic -> Deterministic -> Bool
$c/= :: Deterministic -> Deterministic -> Bool
/= :: Deterministic -> Deterministic -> Bool
Eq, Int -> Deterministic -> ShowS
[Deterministic] -> ShowS
Deterministic -> String
(Int -> Deterministic -> ShowS)
-> (Deterministic -> String)
-> ([Deterministic] -> ShowS)
-> Show Deterministic
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Deterministic -> ShowS
showsPrec :: Int -> Deterministic -> ShowS
$cshow :: Deterministic -> String
show :: Deterministic -> String
$cshowList :: [Deterministic] -> ShowS
showList :: [Deterministic] -> ShowS
Show)

{- | Significance level paired with critical value. ADF rejects for smaller
statistics, KPSS for larger ones. No interpolated p value is fabricated.
-}
data Stationarity = Stationarity
    { Stationarity -> Double
stationarityStatistic :: Double
    -- ^ Computed ADF/KPSS stationarity test statistic.
    , Stationarity -> [(Double, Double)]
stationarityCriticalValues :: [(Double, Double)]
    -- ^ Significance-level and critical-value pairs for the selected test/deterministic specification.
    , Stationarity -> Int
stationarityObservations :: Int
    -- ^ Number of effective observations used by the test.
    , Stationarity -> String
stationarityNull :: String
    -- ^ Human-readable null hypothesis, which differs between ADF and KPSS.
    }
    deriving (Stationarity -> Stationarity -> Bool
(Stationarity -> Stationarity -> Bool)
-> (Stationarity -> Stationarity -> Bool) -> Eq Stationarity
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Stationarity -> Stationarity -> Bool
== :: Stationarity -> Stationarity -> Bool
$c/= :: Stationarity -> Stationarity -> Bool
/= :: Stationarity -> Stationarity -> Bool
Eq, Int -> Stationarity -> ShowS
[Stationarity] -> ShowS
Stationarity -> String
(Int -> Stationarity -> ShowS)
-> (Stationarity -> String)
-> ([Stationarity] -> ShowS)
-> Show Stationarity
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Stationarity -> ShowS
showsPrec :: Int -> Stationarity -> ShowS
$cshow :: Stationarity -> String
show :: Stationarity -> String
$cshowList :: [Stationarity] -> ShowS
showList :: [Stationarity] -> ShowS
Show)

{- | OLS of delta x_t on x_(t-1), an intercept, optional trend, and explicitly
supplied lagged differences. Returns the lagged-level t statistic. Critical
values use MacKinnon (2010) finite-sample polynomials for one series:
https://www.statsmodels.org/stable/generated/statsmodels.tsa.stattools.adfuller.html .
There is no automatic lag selection or normal/Student-t p-value shortcut.
-}
augmentedDickeyFuller :: Deterministic -> Int -> [Double] -> Either MathError Stationarity
augmentedDickeyFuller :: Deterministic -> Int -> [Double] -> Either MathError Stationarity
augmentedDickeyFuller Deterministic
deterministic Int
lags [Double]
xs = do
    Int -> [Double] -> Either MathError ()
validate Int
4 [Double]
xs
    Bool -> String -> Either MathError ()
require (Int
lags Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Int
lags Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
64 Bool -> Bool -> Bool
&& [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
4096) String
"ADF lag/sample limit"
    let deltas :: [Double]
deltas = (Double -> Double -> Double) -> [Double] -> [Double] -> [Double]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (-) ((Int -> [Double] -> [Double]
forall a. Int -> [a] -> [a]
drop Int
1) [Double]
xs) [Double]
xs
        rows :: [[Double]]
rows =
            [ [Double]
xs [Double] -> Int -> Double
forall a. HasCallStack => [a] -> Int -> a
!! Int
t
                Double -> [Double] -> [Double]
forall a. a -> [a] -> [a]
: Deterministic -> Double -> [Double]
deterministicRow Deterministic
deterministic (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
t Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))
                [Double] -> [Double] -> [Double]
forall a. [a] -> [a] -> [a]
++ [[Double]
deltas [Double] -> Int -> Double
forall a. HasCallStack => [a] -> Int -> a
!! (Int
t Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
j) | Int
j <- [Int
1 .. Int
lags]]
            | Int
t <- [Int
lags .. [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
deltas Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
            ]
        target :: [Double]
target = Int -> [Double] -> [Double]
forall a. Int -> [a] -> [a]
drop Int
lags [Double]
deltas
        n :: Int
n = [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
target
        width :: Int
width = Int
lags Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ if Deterministic
deterministic Deterministic -> Deterministic -> Bool
forall a. Eq a => a -> a -> Bool
== Deterministic
LinearTrend then Int
1 else Int
0
    Bool -> String -> Either MathError ()
require (Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
width) String
"insufficient ADF residual degrees of freedom"
    (b, residual, gramInverse) <- [[Double]]
-> [Double] -> Either MathError ([Double], [Double], [[Double]])
regression [[Double]]
rows [Double]
target
    let variance = [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^ (Int
2 :: Int)) [Double]
residual) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
width)
        se = Double -> Double
forall a. Floating a => a -> a
sqrt (Double
variance Double -> Double -> Double
forall a. Num a => a -> a -> a
* ([Double] -> Int -> Double
forall a. HasCallStack => [a] -> Int -> a
!! Int
0) (([[Double]] -> Int -> [Double]
forall a. HasCallStack => [a] -> Int -> a
!! Int
0) [[Double]]
gramInverse))
    require (finite se && se > 0) "degenerate ADF residual variance"
    stat <- (!! 0) <$> checked [(!! 0) b / se]
    let coefficients =
            if Deterministic
deterministic Deterministic -> Deterministic -> Bool
forall a. Eq a => a -> a -> Bool
== Deterministic
Level
                then
                    [ [-Double
3.43035, -Double
6.5393, -Double
16.786, -Double
79.433]
                    , [-Double
2.86154, -Double
2.8903, -Double
4.234, -Double
40.040]
                    , [-Double
2.56677, -Double
1.5384, -Double
2.809, Double
0]
                    ]
                else
                    [ [-Double
3.95877, -Double
9.0531, -Double
28.428, -Double
134.155]
                    , [-Double
3.41049, -Double
4.3904, -Double
9.036, -Double
45.374]
                    , [-Double
3.12705, -Double
2.5856, -Double
3.925, -Double
22.380]
                    ]
        critical [a]
row = [a] -> a
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [a
v a -> a -> a
forall a. Fractional a => a -> a -> a
/ (Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n a -> Int -> a
forall a b. (Num a, Integral b) => a -> b -> a
^ Int
k) | (Int
k, a
v) <- [Int] -> [a] -> [(Int, a)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0 :: Int ..] [a]
row]
    Right (Stationarity stat (zip [0.01, 0.05, 0.1] (map critical coefficients)) n "unit root")

{- | KPSS level/trend stationarity with an explicit Bartlett/Newey--West lag
bandwidth. Critical values are Table 1 of Kwiatkowski et al. (1992), as
documented at https://www.statsmodels.org/stable/generated/statsmodels.tsa.stattools.kpss.html .
-}
kpss :: Deterministic -> Int -> [Double] -> Either MathError Stationarity
kpss :: Deterministic -> Int -> [Double] -> Either MathError Stationarity
kpss Deterministic
deterministic Int
lags [Double]
xs = do
    Int -> [Double] -> Either MathError ()
validate Int
4 [Double]
xs
    Bool -> String -> Either MathError ()
require
        ( Int
lags Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0
            Bool -> Bool -> Bool
&& Int
lags Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs
            Bool -> Bool -> Bool
&& [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
4096
            Bool -> Bool -> Bool
&& Int -> Integer
forall a. Integral a => a -> Integer
toInteger Int
lags Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Int -> Integer
forall a. Integral a => a -> Integer
toInteger ([Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs) Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
4000000
        )
        String
"KPSS lag/sample limit"
    let n :: Int
n = [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs; nf :: Double
nf = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n
    residual <- case Deterministic
deterministic of
        Deterministic
Level -> [Double] -> Either MathError [Double]
forall a b. b -> Either a b
Right ((Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double -> Double -> Double
forall a. Num a => a -> a -> a
subtract ([Double] -> Double
average [Double]
xs)) [Double]
xs)
        Deterministic
LinearTrend -> do
            (_, rs, _) <- [[Double]]
-> [Double] -> Either MathError ([Double], [Double], [[Double]])
regression [Deterministic -> Double -> [Double]
deterministicRow Deterministic
deterministic (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
i) | Int
i <- [Int
1 .. Int
n]] [Double]
xs
            Right rs
    let energy = [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^ (Int
2 :: Int)) [Double]
residual)
        longRun =
            ( Double
energy
                Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
2
                    Double -> Double -> Double
forall a. Num a => a -> a -> a
* [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum
                        [ (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
k Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
lags Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)) Double -> Double -> Double
forall a. Num a => a -> a -> a
* [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Double -> Double -> Double) -> [Double] -> [Double] -> [Double]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Double -> Double -> Double
forall a. Num a => a -> a -> a
(*) (Int -> [Double] -> [Double]
forall a. Int -> [a] -> [a]
drop Int
k [Double]
residual) [Double]
residual)
                        | Int
k <- [Int
1 .. Int
lags]
                        ]
            )
                Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
nf
        partial = (Int -> [Double] -> [Double]
forall a. Int -> [a] -> [a]
drop Int
1) ((Double -> Double -> Double) -> Double -> [Double] -> [Double]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl Double -> Double -> Double
forall a. Num a => a -> a -> a
(+) Double
0 [Double]
residual)
    require (finite longRun && longRun > 1e-28 * max 1 energy) "degenerate KPSS long-run variance"
    stat <- (!! 0) <$> checked [sum (map (^ (2 :: Int)) partial) / (nf * nf * longRun)]
    let critical = if Deterministic
deterministic Deterministic -> Deterministic -> Bool
forall a. Eq a => a -> a -> Bool
== Deterministic
Level then [Double
0.347, Double
0.463, Double
0.574, Double
0.739] else [Double
0.119, Double
0.146, Double
0.176, Double
0.216]
    Right (Stationarity stat (zip [0.1, 0.05, 0.025, 0.01] critical) n "stationarity")

deterministicRow :: Deterministic -> Double -> [Double]
deterministicRow :: Deterministic -> Double -> [Double]
deterministicRow Deterministic
Level Double
_ = [Double
1]
deterministicRow Deterministic
LinearTrend Double
t = [Double
1, Double
t]
regression :: [[Double]] -> [Double] -> Either MathError ([Double], [Double], [[Double]])
regression :: [[Double]]
-> [Double] -> Either MathError ([Double], [Double], [[Double]])
regression [[Double]]
rows [Double]
target = do
    let n :: Int
n = [[Double]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[Double]]
rows; p :: Int
p = [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (([[Double]] -> Int -> [Double]
forall a. HasCallStack => [a] -> Int -> a
!! Int
0) [[Double]]
rows)
    matrix <- Int -> Int -> [Double] -> Either MathError (Matrix Double)
forall a. Int -> Int -> [a] -> Either MathError (Matrix a)
M.fromListChecked Int
n Int
p ([[Double]] -> [Double]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Double]]
rows)
    b <- QR.leastSquares matrix target
    let residual = (Double -> Double -> Double) -> [Double] -> [Double] -> [Double]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (-) [Double]
target [[Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Double -> Double -> Double) -> [Double] -> [Double] -> [Double]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Double -> Double -> Double
forall a. Num a => a -> a -> a
(*) [Double]
row [Double]
b) | [Double]
row <- [[Double]]
rows]
        gram = [[[Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Double -> Double -> Double) -> [Double] -> [Double] -> [Double]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Double -> Double -> Double
forall a. Num a => a -> a -> a
(*) [Double]
a [Double]
c) | [Double]
c <- [[Double]] -> [[Double]]
forall a. [[a]] -> [[a]]
transpose [[Double]]
rows] | [Double]
a <- [[Double]] -> [[Double]]
forall a. [[a]] -> [[a]]
transpose [[Double]]
rows]
    gm <- M.fromListChecked p p (concat gram)
    inverse <- LU.inverse gm
    let iv = [Int -> [Double] -> [Double]
forall a. Int -> [a] -> [a]
take Int
p (Int -> [Double] -> [Double]
forall a. Int -> [a] -> [a]
drop (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
p) (Matrix Double -> [Double]
forall a. Matrix a -> [a]
M.toList Matrix Double
inverse)) | Int
i <- [Int
0 .. Int
p Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]]
    _ <- checked (b ++ residual ++ concat iv)
    Right (b, residual, iv)

{- | Exact quadratic dynamic programming for piecewise-constant least squares,
minimizing total within-segment SSE plus penalty per change. Break indices
are zero-based starts of new segments. No pruning/approximation is used.
-}
changePoints :: Double -> Int -> [Double] -> Either MathError [Int]
changePoints :: Double -> Int -> [Double] -> Either MathError [Int]
changePoints Double
penalty Int
minSegment [Double]
xs = do
    Int -> [Double] -> Either MathError ()
validate Int
1 [Double]
xs
    let n :: Int
n = [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs
    Bool -> String -> Either MathError ()
require
        (Double -> Bool
finite Double
penalty Bool -> Bool -> Bool
&& Double
penalty Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
0 Bool -> Bool -> Bool
&& Int
minSegment Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
1 Bool -> Bool -> Bool
&& Int
minSegment Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
n Bool -> Bool -> Bool
&& Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
2048)
        String
"change-point configuration/sample limit"
    -- Centering before prefix sums avoids severe cancellation for shifted data.
    let centered :: [Double]
centered = (Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double -> Double -> Double
forall a. Num a => a -> a -> a
subtract ([Double] -> Double
average [Double]
xs)) [Double]
xs
        sums :: Array Int Double
sums = [Double] -> Array Int Double
forall {a}. Num a => [a] -> Array Int a
prefix [Double]
centered
        squares :: Array Int Double
squares = [Double] -> Array Int Double
forall {a}. Num a => [a] -> Array Int a
prefix ((Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^ (Int
2 :: Int)) [Double]
centered)
        cost :: Int -> Int -> Double
cost Int
i Int
j =
            let s :: Double
s = Array Int Double
sums Array Int Double -> Int -> Double
forall i e. Ix i => Array i e -> i -> e
! Int
j Double -> Double -> Double
forall a. Num a => a -> a -> a
- Array Int Double
sums Array Int Double -> Int -> Double
forall i e. Ix i => Array i e -> i -> e
! Int
i; q :: Double
q = Array Int Double
squares Array Int Double -> Int -> Double
forall i e. Ix i => Array i e -> i -> e
! Int
j Double -> Double -> Double
forall a. Num a => a -> a -> a
- Array Int Double
squares Array Int Double -> Int -> Double
forall i e. Ix i => Array i e -> i -> e
! Int
i
             in Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
0 (Double
q Double -> Double -> Double
forall a. Num a => a -> a -> a
- (Double
s Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double -> Double
forall a. Floating a => a -> a
sqrt (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
i))) Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^ (Int
2 :: Int))
        solution :: Array Int (Double, [Int])
        solution :: Array Int (Double, [Int])
solution = (Int, Int) -> [(Int, (Double, [Int]))] -> Array Int (Double, [Int])
forall i e. Ix i => (i, i) -> [(i, e)] -> Array i e
array (Int
0, Int
n) [(Int
j, Int -> (Double, [Int])
entry Int
j) | Int
j <- [Int
0 .. Int
n]]
        entry :: Int -> (Double, [Int])
entry Int
0 = (-Double
penalty, [])
        entry Int
j = case [ (Double
base Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Int -> Int -> Double
cost Int
i Int
j Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
penalty, if Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then [] else [Int]
breaks [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [Int
i])
                       | Int
i <- [Int
0 .. Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
minSegment]
                       , Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
|| Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
minSegment
                       , let (Double
base, [Int]
breaks) = Array Int (Double, [Int])
solution Array Int (Double, [Int]) -> Int -> (Double, [Int])
forall i e. Ix i => Array i e -> i -> e
! Int
i
                       , Double -> Bool
finite Double
base
                       ] of
            [] -> (Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
0, [])
            [(Double, [Int])]
candidates -> ((Double, [Int]) -> (Double, [Int]) -> Ordering)
-> [(Double, [Int])] -> (Double, [Int])
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> Ordering) -> t a -> a
minimumBy (((Double, [Int]) -> Double)
-> (Double, [Int]) -> (Double, [Int]) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (Double, [Int]) -> Double
forall a b. (a, b) -> a
fst) [(Double, [Int])]
candidates
    _ <- [Double] -> Either MathError [Double]
checked ([Double]
centered [Double] -> [Double] -> [Double]
forall a. [a] -> [a] -> [a]
++ Array Int Double -> [Double]
forall {a}. Array Int a -> [a]
elemsOf Array Int Double
sums [Double] -> [Double] -> [Double]
forall a. [a] -> [a] -> [a]
++ Array Int Double -> [Double]
forall {a}. Array Int a -> [a]
elemsOf Array Int Double
squares)
    let (value, breaks) = solution ! n
    _ <- checked [value]
    Right breaks
  where
    prefix :: [a] -> Array Int a
prefix [a]
ys = (Int, Int) -> [(Int, a)] -> Array Int a
forall i e. Ix i => (i, i) -> [(i, e)] -> Array i e
array (Int
0, [a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
ys) ([Int] -> [a] -> [(Int, a)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0 ..] ((a -> a -> a) -> a -> [a] -> [a]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl a -> a -> a
forall a. Num a => a -> a -> a
(+) a
0 [a]
ys))
    elemsOf :: Array Int a -> [a]
elemsOf Array Int a
a = [Array Int a
a Array Int a -> Int -> a
forall i e. Ix i => Array i e -> i -> e
! Int
i | Int
i <- [Int
0 .. [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs]]

{- | Two-sided Page CUSUM with supplied baseline mean/SD. A threshold crossing
emits its index and resets both cumulative scores. drift and threshold are
in standardized observation units.
-}
cusum :: Double -> Double -> Double -> Double -> [Double] -> Either MathError [Int]
cusum :: Double
-> Double -> Double -> Double -> [Double] -> Either MathError [Int]
cusum Double
mean Double
sd Double
drift Double
threshold [Double]
xs = do
    Int -> [Double] -> Either MathError ()
validate Int
0 [Double]
xs
    Bool -> String -> Either MathError ()
require
        ((Double -> Bool) -> [Double] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Double -> Bool
finite [Double
mean, Double
sd, Double
drift, Double
threshold] Bool -> Bool -> Bool
&& Double
sd Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
0 Bool -> Bool -> Bool
&& Double
drift Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
0 Bool -> Bool -> Bool
&& Double
threshold Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
0)
        String
"invalid CUSUM parameters"
    let step :: (a, b, [a])
-> (a, Double) -> Either MathError (Double, Double, [a])
step (a
plus, b
minus, [a]
alerts) (a
i, Double
x) = do
            let z :: Double
z = (Double
x Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
mean) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
sd
            _ <- [Double] -> Either MathError [Double]
checked [Double
z]
            -- Exact three-term accumulation avoids overflow before a cancelling
            -- drift. Validate the rounded scores before a threshold can reset them.
            let score a
old a
delta = Rational -> a
forall a. Fractional a => Rational -> a
fromRational (Rational -> Rational -> Rational
forall a. Ord a => a -> a -> a
max Rational
0 (a -> Rational
forall a. Real a => a -> Rational
toRational a
old Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
+ a -> Rational
forall a. Real a => a -> Rational
toRational a
delta Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
- Double -> Rational
forall a. Real a => a -> Rational
toRational Double
drift))
                p = a -> Double -> Double
forall {a} {a} {a}. (Fractional a, Real a, Real a) => a -> a -> a
score a
plus Double
z
                m = b -> Double -> Double
forall {a} {a} {a}. (Fractional a, Real a, Real a) => a -> a -> a
score b
minus (-Double
z)
            _ <- checked [p, m]
            Right (if max p m > threshold then (0, 0, i : alerts) else (p, m, alerts))
    (_, _, finalAlerts) <- ((Double, Double, [Int])
 -> (Int, Double) -> Either MathError (Double, Double, [Int]))
-> (Double, Double, [Int])
-> [(Int, Double)]
-> Either MathError (Double, Double, [Int])
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM (Double, Double, [Int])
-> (Int, Double) -> Either MathError (Double, Double, [Int])
forall {a} {b} {a}.
(Real a, Real b) =>
(a, b, [a])
-> (a, Double) -> Either MathError (Double, Double, [a])
step (Double
0, Double
0, []) ([Int] -> [Double] -> [(Int, Double)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0 ..] [Double]
xs)
    Right (reverse finalAlerts)

{- | Median/MAD outliers, using the normal-consistency factor 1.4826022185.
If MAD is zero, every value different from the median is an anomaly.
-}
anomalies :: Double -> [Double] -> Either MathError [Int]
anomalies :: Double -> [Double] -> Either MathError [Int]
anomalies Double
threshold [Double]
xs = do
    Int -> [Double] -> Either MathError ()
validate Int
1 [Double]
xs
    Bool -> String -> Either MathError ()
require (Double -> Bool
finite Double
threshold Bool -> Bool -> Bool
&& Double
threshold Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
0) String
"positive anomaly threshold required"
    median <- [Double] -> Either MathError Double
Stats.median [Double]
xs
    mad <- Stats.median (map (abs . subtract median) xs)
    differences <- checked (map (subtract median) xs)
    scores <-
        if mad == 0
            then Right (replicate (length xs) 0)
            else checked [abs delta / mad / 1.4826022185 | delta <- differences]
    Right
        [i | (i, (x, z)) <- zip [0 ..] (zip xs scores), if mad == 0 then x /= median else z > threshold]

{- | Dynamic-time-warping accumulated distance and original-series zero-based alignment
path.
-}
data DTWResult = DTWResult
    { DTWResult -> Double
dtwDistance :: Double
    -- ^ Accumulated dynamic-time-warping path cost.
    , DTWResult -> [(Int, Int)]
dtwPath :: [(Int, Int)]
    -- ^ Optimal alignment as zero-based index pairs from the two input series.
    }
    deriving (DTWResult -> DTWResult -> Bool
(DTWResult -> DTWResult -> Bool)
-> (DTWResult -> DTWResult -> Bool) -> Eq DTWResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DTWResult -> DTWResult -> Bool
== :: DTWResult -> DTWResult -> Bool
$c/= :: DTWResult -> DTWResult -> Bool
/= :: DTWResult -> DTWResult -> Bool
Eq, Int -> DTWResult -> ShowS
[DTWResult] -> ShowS
DTWResult -> String
(Int -> DTWResult -> ShowS)
-> (DTWResult -> String)
-> ([DTWResult] -> ShowS)
-> Show DTWResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DTWResult -> ShowS
showsPrec :: Int -> DTWResult -> ShowS
$cshow :: DTWResult -> String
show :: DTWResult -> String
$cshowList :: [DTWResult] -> ShowS
showList :: [DTWResult] -> ShowS
Show)

{- | Absolute local cost; Sakoe--Chiba radius must include both endpoints.
Returns the unnormalized path cost, with diagonal/up/left tie priority.
-}
dynamicTimeWarping :: Int -> [Double] -> [Double] -> Either MathError DTWResult
dynamicTimeWarping :: Int -> [Double] -> [Double] -> Either MathError DTWResult
dynamicTimeWarping Int
radius [Double]
xs [Double]
ys = do
    Int -> [Double] -> Either MathError ()
validate Int
1 [Double]
xs
    Int -> [Double] -> Either MathError ()
validate Int
1 [Double]
ys
    let n :: Int
n = [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs; m :: Int
m = [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
ys
    Bool -> String -> Either MathError ()
require (Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
512 Bool -> Bool -> Bool
&& Int
m Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
512 Bool -> Bool -> Bool
&& Int
radius Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int -> Int
forall a. Num a => a -> a
abs (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
m) Bool -> Bool -> Bool
&& Int
radius Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
16384) String
"DTW size/radius limit"
    let table :: Array (Int, Int) (Double, (Int, Int))
        table :: Array (Int, Int) (Double, (Int, Int))
table = ((Int, Int), (Int, Int))
-> [((Int, Int), (Double, (Int, Int)))]
-> Array (Int, Int) (Double, (Int, Int))
forall i e. Ix i => (i, i) -> [(i, e)] -> Array i e
array ((Int
0, Int
0), (Int
n, Int
m)) [((Int
i, Int
j), Int -> Int -> (Double, (Int, Int))
entry Int
i Int
j) | Int
i <- [Int
0 .. Int
n], Int
j <- [Int
0 .. Int
m]]
        entry :: Int -> Int -> (Double, (Int, Int))
entry Int
0 Int
0 = (Double
0, (Int
0, Int
0))
        entry Int
i Int
j
            | Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
|| Int
j Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
|| Int -> Int
forall a. Num a => a -> a
abs (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
j) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
radius = (Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
0, (Int
0, Int
0))
            | Bool
otherwise =
                let choices :: [(Double, (Int, Int))]
choices = [((Double, (Int, Int)) -> Double
forall a b. (a, b) -> a
fst (Array (Int, Int) (Double, (Int, Int))
table Array (Int, Int) (Double, (Int, Int))
-> (Int, Int) -> (Double, (Int, Int))
forall i e. Ix i => Array i e -> i -> e
! (Int
a, Int
b)), (Int
a, Int
b)) | (Int
a, Int
b) <- [(Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1, Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1), (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1, Int
j), (Int
i, Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)]]
                    (Double
cost, (Int, Int)
predecessor) = ((Double, (Int, Int)) -> (Double, (Int, Int)) -> Ordering)
-> [(Double, (Int, Int))] -> (Double, (Int, Int))
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> Ordering) -> t a -> a
minimumBy (((Double, (Int, Int)) -> Double)
-> (Double, (Int, Int)) -> (Double, (Int, Int)) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (Double, (Int, Int)) -> Double
forall a b. (a, b) -> a
fst) [(Double, (Int, Int))]
choices
                 in (Double
cost Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double -> Double
forall a. Num a => a -> a
abs ([Double]
xs [Double] -> Int -> Double
forall a. HasCallStack => [a] -> Int -> a
!! (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Double -> Double -> Double
forall a. Num a => a -> a -> a
- [Double]
ys [Double] -> Int -> Double
forall a. HasCallStack => [a] -> Int -> a
!! (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)), (Int, Int)
predecessor)
        trace :: (Int, Int) -> [(Int, Int)] -> [(Int, Int)]
trace (Int
0, Int
0) [(Int, Int)]
acc = [(Int, Int)]
acc
        trace ij :: (Int, Int)
ij@(Int
i, Int
j) [(Int, Int)]
acc = (Int, Int) -> [(Int, Int)] -> [(Int, Int)]
trace ((Double, (Int, Int)) -> (Int, Int)
forall a b. (a, b) -> b
snd (Array (Int, Int) (Double, (Int, Int))
table Array (Int, Int) (Double, (Int, Int))
-> (Int, Int) -> (Double, (Int, Int))
forall i e. Ix i => Array i e -> i -> e
! (Int, Int)
ij)) ((Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1, Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (Int, Int) -> [(Int, Int)] -> [(Int, Int)]
forall a. a -> [a] -> [a]
: [(Int, Int)]
acc)
        distance :: Double
distance = (Double, (Int, Int)) -> Double
forall a b. (a, b) -> a
fst (Array (Int, Int) (Double, (Int, Int))
table Array (Int, Int) (Double, (Int, Int))
-> (Int, Int) -> (Double, (Int, Int))
forall i e. Ix i => Array i e -> i -> e
! (Int
n, Int
m))
    _ <- [Double] -> Either MathError [Double]
checked [Double
distance]
    Right (DTWResult distance (trace (n, m) []))

{- | One-sided unwindowed periodogram density. Non-DC/Nyquist bins are doubled;
integral of density over frequency equals the sample mean square.
-}
spectrum :: Double -> [Double] -> Either MathError [(Double, Double)]
spectrum :: Double -> [Double] -> Either MathError [(Double, Double)]
spectrum Double
sampleRate [Double]
xs = do
    Int -> [Double] -> Either MathError ()
validate Int
1 [Double]
xs
    Bool -> String -> Either MathError ()
require (Double -> Bool
finite Double
sampleRate Bool -> Bool -> Bool
&& Double
sampleRate Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
0) String
"positive sample rate required"
    bins <- [Complex Double] -> Either MathError [Complex Double]
F.fft ((Double -> Complex Double) -> [Double] -> [Complex Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double -> Double -> Complex Double
forall a. a -> a -> Complex a
:+ Double
0) [Double]
xs)
    let n = [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs
        density =
            [ ( Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
k Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
sampleRate Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n
              , Complex Double -> Double
forall a. RealFloat a => Complex a -> a
magnitude ([Complex Double]
bins [Complex Double] -> Int -> Complex Double
forall a. HasCallStack => [a] -> Int -> a
!! Int
k) Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^ (Int
2 :: Int)
                    Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double
sampleRate Double -> Double -> Double
forall a. Num a => a -> a -> a
* Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n)
                    Double -> Double -> Double
forall a. Num a => a -> a -> a
* (if Int
k Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
|| Int -> Bool
forall a. Integral a => a -> Bool
even Int
n Bool -> Bool -> Bool
&& Int
k Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2 then Double
1 else Double
2)
              )
            | Int
k <- [Int
0 .. Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2]
            ]
    _ <- checked (concatMap (\(Double
a, Double
b) -> [Double
a, Double
b]) density)
    Right density

{- | Prediction-error metric selector; scale/zero-denominator preconditions are checked by
the metric function.
-}
data ForecastMetric = MAE | MSE | RMSE | MAPE | SMAPE | MASE Int [Double] deriving (ForecastMetric -> ForecastMetric -> Bool
(ForecastMetric -> ForecastMetric -> Bool)
-> (ForecastMetric -> ForecastMetric -> Bool) -> Eq ForecastMetric
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ForecastMetric -> ForecastMetric -> Bool
== :: ForecastMetric -> ForecastMetric -> Bool
$c/= :: ForecastMetric -> ForecastMetric -> Bool
/= :: ForecastMetric -> ForecastMetric -> Bool
Eq, Int -> ForecastMetric -> ShowS
[ForecastMetric] -> ShowS
ForecastMetric -> String
(Int -> ForecastMetric -> ShowS)
-> (ForecastMetric -> String)
-> ([ForecastMetric] -> ShowS)
-> Show ForecastMetric
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ForecastMetric -> ShowS
showsPrec :: Int -> ForecastMetric -> ShowS
$cshow :: ForecastMetric -> String
show :: ForecastMetric -> String
$cshowList :: [ForecastMetric] -> ShowS
showList :: [ForecastMetric] -> ShowS
Show)

{- | MAPE and sMAPE are percentages; zero/zero sMAPE contributes zero. MASE
supplies its separate training set and seasonal lag to avoid test leakage.
-}
forecastError :: ForecastMetric -> [Double] -> [Double] -> Either MathError Double
forecastError :: ForecastMetric -> [Double] -> [Double] -> Either MathError Double
forecastError ForecastMetric
metric [Double]
actual [Double]
predicted = do
    Int -> [Double] -> Either MathError ()
validate Int
1 [Double]
actual
    Int -> [Double] -> Either MathError ()
validate Int
1 [Double]
predicted
    Bool -> String -> Either MathError ()
require ([Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
actual Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== [Double] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
predicted) String
"forecast metric length mismatch"
    let errors :: [Double]
errors = (Double -> Double -> Double) -> [Double] -> [Double] -> [Double]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (-) [Double]
predicted [Double]
actual; mse :: Double
mse = [Double] -> Double
average ((Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^ (Int
2 :: Int)) [Double]
errors)
    value <- case ForecastMetric
metric of
        ForecastMetric
MAE -> Double -> Either MathError Double
forall a b. b -> Either a b
Right ([Double] -> Double
average ((Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map Double -> Double
forall a. Num a => a -> a
abs [Double]
errors))
        ForecastMetric
MSE -> Double -> Either MathError Double
forall a b. b -> Either a b
Right Double
mse
        ForecastMetric
RMSE -> Double -> Either MathError Double
forall a b. b -> Either a b
Right (Double -> Double
forall a. Floating a => a -> a
sqrt Double
mse)
        ForecastMetric
MAPE -> do
            Bool -> String -> Either MathError ()
require ((Double -> Bool) -> [Double] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
/= Double
0) [Double]
actual) String
"MAPE actual values must be nonzero"
            Double -> Either MathError Double
forall a b. b -> Either a b
Right (Double
100 Double -> Double -> Double
forall a. Num a => a -> a -> a
* [Double] -> Double
average ((Double -> Double -> Double) -> [Double] -> [Double] -> [Double]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Double
e Double
x -> Double -> Double
forall a. Num a => a -> a
abs (Double
e Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
x)) [Double]
errors [Double]
actual))
        ForecastMetric
SMAPE ->
            Double -> Either MathError Double
forall a b. b -> Either a b
Right
                ( Double
100
                    Double -> Double -> Double
forall a. Num a => a -> a -> a
* [Double] -> Double
average
                        [ if Double
x Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
0 Bool -> Bool -> Bool
&& Double
y Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
0
                            then Double
0
                            else Double
2 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double -> Double
forall a. Num a => a -> a
abs (Double
x Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
scale Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
y Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
scale) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double -> Double
forall a. Num a => a -> a
abs (Double
x Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
scale) Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double -> Double
forall a. Num a => a -> a
abs (Double
y Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
scale))
                        | (Double
x, Double
y) <- [Double] -> [Double] -> [(Double, Double)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Double]
actual [Double]
predicted
                        , let scale :: Double
scale = Double -> Double -> Double
forall a. Ord a => a -> a -> a
max (Double -> Double
forall a. Num a => a -> a
abs Double
x) (Double -> Double
forall a. Num a => a -> a
abs Double
y)
                        ]
                )
        MASE Int
lag [Double]
training -> do
            deltas <- Int -> Int -> [Double] -> Either MathError [Double]
difference Int
1 Int
lag [Double]
training
            let scale = [Double] -> Double
average ((Double -> Double) -> [Double] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map Double -> Double
forall a. Num a => a -> a
abs [Double]
deltas)
            require (finite scale && scale > 0) "MASE requires nonzero training scale"
            Right (average (map abs errors) / scale)
    (!! 0) <$> checked [value]