packages feed

hasquant-0.8.0.0: test/hspec/QuantLib/Spec/Method.hs

-- Longstaff-Schwartz regression split into fit and evaluate: stored coefficients must reproduce
-- the combined calls bit for bit, the one-column scalar basis included. States stay inside (-1, 1),
-- where Chebyshev's weighted basis is defined.
module QuantLib.Spec.Method (spec) where

import Control.Exception (evaluate, try, SomeException)
import Control.Monad (forM_)
import Data.Either (isLeft)
import qualified Data.Vector.Storable as V
import GHC.Float (castDoubleToWord64)
import Test.Hspec

import QuantLib.Math
import QuantLib.Method

spec :: Spec
spec = describe "Longstaff-Schwartz fit and evaluate (QuantLib.Method)" $ do
  it "reproduces the combined regression bit for bit" $
    forM_ [(p, o, d) | p <- [Monomial, Laguerre, Hermite, Legendre, Chebyshev], o <- [1, 2, 3], d <- [1, 2, 3]] $ \(p, o, d) -> do
      let fitStates = states d fitRows 0.6180339887
          evalStates = states d evalRows 0.4142135623
          targets = V.fromList [target (row d 0.6180339887 i) | i <- [0 .. fitRows - 1]]
      coefficients <- lsmFit p o (matrix d fitRows fitStates) targets
      split <- lsmEvaluate p o coefficients (matrix d evalRows evalStates)
      combined <- if d == 1
        then lsmRegress p o (V.fromList fitStates) targets (V.fromList evalStates)
        else lsmRegressMulti p o (matrix d fitRows fitStates) targets (matrix d evalRows evalStates)
      (p, o, d, V.length coefficients) `shouldBe` (p, o, d, fromIntegral (lsmBasisSize d o))
      (p, o, d, bits split) `shouldBe` (p, o, d, bits combined)
  it "refuses coefficients that do not fit the basis" $ do
    coefficients <- lsmFit Monomial 2 (matrix 2 fitRows (states 2 fitRows 0.6180339887))
      (V.fromList [target (row 2 0.6180339887 i) | i <- [0 .. fitRows - 1]])
    result <- try (lsmEvaluate Monomial 2 (V.init coefficients) (matrix 2 evalRows (states 2 evalRows 0.4142135623)) >>= evaluate)
    isLeft (result :: Either SomeException RealVector) `shouldBe` True
  it "names its build" $
    lsmEvaluatorIdentity `shouldContain` "QuantLib"
  where
    fitRows = 40
    evalRows = 7
    bits = map castDoubleToWord64 . V.toList
    target xs = sum (map (exp . negate) xs) + product xs
    row :: Word -> Double -> Word -> [Double]
    row d step i = [1.6 * snd (properFraction (fromIntegral (i + 1) * step * fromIntegral k) :: (Int, Double)) - 0.8 | k <- [1 .. d]]
    states d n step = concat [row d step i | i <- [0 .. n - 1]]
    matrix d n xs = either error id (realMatrixFromVector n d (V.fromList xs))