packages feed

markov-chain-usage-model (empty) → 0.0.0

raw patch · 8 files changed

+716/−0 lines, 8 filesdep +basedep +doctestdep +markov-chain-usage-modelsetup-changed

Dependencies added: base, doctest, markov-chain-usage-model, matrix, tasty, tasty-discover, tasty-hunit, vector

Files

+ CHANGELOG.md view
@@ -0,0 +1,3 @@+#### 0.0.0++* Initial release.
+ LICENSE view
@@ -0,0 +1,26 @@+Copyright (C) 2018-2019 Stevan Andjelkovic, Robert Danitz++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,72 @@+## markov-chain-usage-model++[![Hackage](https://img.shields.io/hackage/v/markov-chain-usage-model.svg)](https://hackage.haskell.org/package/markov-chain-usage-model)+[![Stackage Nightly](http://stackage.org/package/markov-chain-usage-model/badge/nightly)](http://stackage.org/nightly/package/markov-chain-usage-model)+[![Stackage LTS](http://stackage.org/package/markov-chain-usage-model/badge/lts)](http://stackage.org/lts/package/markov-chain-usage-model)+[![Build Status](https://api.travis-ci.org/advancedtelematic/markov-chain-usage-model.svg?branch=master)](https://travis-ci.org/advancedtelematic/markov-chain-usage-model)++`markov-chain-usage-model` is a Haskell library for performing computations on+Markov chain based usage models.++### References++  * [A theory of software reliability and its+    application](http://doi.ieeecomputersociety.org/10.1109/TSE.1975.6312856)+    (1975) by J. D. Musa;++  * [Engineering software under statistical quality+    control](https://doi.org/10.1109/52.60601) (1990) by R. H. Cobb and H. D.+    Mills;++  * [Operational profiles in software-reliability+    engineering](https://doi.org/10.1109/52.199724) (1993) by J. D. Musa;++  * [A Markov chain model for statistical software+    testing](https://doi.org/10.1109/32.328991) (1994) by J. A. Whittaker and M.+    G. Thomason;++  * [Statistical testing of software based on a usage+    model](https://doi.org/10.1002/spe.4380250106) (1995) by G. H. Walton, J. H.+    Poore and C. J. Trammell;++  * [Quantifying the reliability of software: statistical testing based on a+    usage model](https://doi.org/10.1109/SESS.1995.525966) (1995) by C.+    Trammell;++  * [Computations for Markov Chain Usage+    Models](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.416.2257&rep=rep1&type=pdf)+    (2000) by S. J. Prowell;++  * [Computing system reliability using Markov chain usage+    models](https://doi.org/10.1016/S0164-1212(03)00241-3) (2004) by S. J.+    Prowell and J. H. Poore;++  * Software Reliability Engineering: More Reliable Software Faster and Cheaper+    (2004) by J. D. Musa;++  * [A Simpler and More Direct Derivation of System Reliability Using Markov+    Chain Usage+    Models](https://ksiresearchorg.ipage.com/seke/seke17paper/seke17paper_91.pdf)+    (2017) by L. Lin, Y. Xue and F. Song;++  * [On A Simpler and Faster Derivation of Single Use Reliability Mean and+    Variance for Model-Based Statistical+    Testing](http://ksiresearchorg.ipage.com/seke/seke18paper/seke18paper_26.pdf)+    (2018) by Y. Xue, L. Lin, X. Sun and F. Song;++  * [Stopping criteria for statistical+    testing](https://doi.org/10.1016/S0950-5849(00)00110-5) (2000) by K. Sayre+    and J. H. Poore;++  * [A cost-benefit stopping criterion for statistical+    testing](https://doi.org/10.1109/HICSS.2004.1265715) (2004) by S. J.+    Prowell.++### See also++  * The JUMBL library and tool ([documentation](http://jumbl.sourceforge.net/)+    and [code](https://sourceforge.net/p/jumbl/code/ci/master/tree/))++### License++BSD-style (see the file LICENSE).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ markov-chain-usage-model.cabal view
@@ -0,0 +1,76 @@+cabal-version: 1.12++name:           markov-chain-usage-model+version:        0.0.0+synopsis:       Computations for Markov chain usage models+description:    Please see the README on GitHub at <https://github.com/advancedtelematic/markov-chain-usage-model#readme>+homepage:       https://github.com/advancedtelematic/markov-chain-usage-model#readme+bug-reports:    https://github.com/advancedtelematic/markov-chain-usage-model/issues+author:         Stevan Andjelkovic+maintainer:     stevan.andjelkovic@here.com+copyright:      Copyright (C) 2018-2019, HERE Europe B.V.+category:       Testing+license:        BSD2+license-file:   LICENSE+build-type:     Simple+tested-with:    GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.3+extra-source-files:+    README.md+    CHANGELOG.md++source-repository head+  type: git+  location: https://github.com/advancedtelematic/markov-chain-usage-model++library+  exposed-modules:+      MarkovChain+  other-modules:+      Paths_markov_chain_usage_model+  hs-source-dirs:+      src+  ghc-options:+      -Weverything+      -Wno-missing-exported-signatures+      -Wno-missing-import-lists+      -Wno-missed-specialisations+      -Wno-all-missed-specialisations+      -Wno-unsafe+      -Wno-safe+      -Wno-missing-local-signatures+      -Wno-monomorphism-restriction+  build-depends:+      base >=4.10 && <5+    , matrix >= 0.3.6.1+    , vector >= 0.10+  default-language: Haskell2010++test-suite markov-chain-usage-model-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_markov_chain_usage_model+    , Unit+  hs-source-dirs:+      test+  ghc-options:+      -threaded -rtsopts -with-rtsopts=-N+      -Weverything+      -Wno-missing-exported-signatures+      -Wno-missing-import-lists+      -Wno-missed-specialisations+      -Wno-all-missed-specialisations+      -Wno-unsafe+      -Wno-safe+      -Wno-missing-local-signatures+      -Wno-monomorphism-restriction+  build-depends:+      base >=4.7 && <5+    , doctest+    , markov-chain-usage-model+    , matrix+    , tasty+    , tasty-discover+    , tasty-hunit+    , vector+  default-language: Haskell2010
+ src/MarkovChain.hs view
@@ -0,0 +1,418 @@+module MarkovChain+  ( M+  , V+  , (.*)+  , (./)+  , (.-)+  , (^*)+  , norm_1+  , norm_F+  , fundamental+  , occVariance+  , perron+  , sigma+  , nodeProbabilities+  , expectedLength+  , expectedArcReliability+  , transientReliability+  , singleUseReliability+  -- , singleUseReliabilityIO+  -- , loadStaticMatrix+  -- , load2StaticMatrices+  -- , saveStaticMatrix+  , kullbackLeibler+  )+  where++import           Data.Bifunctor+                   (bimap)+import qualified Data.List          as List+import           Data.Matrix        as Matrix+import           Data.Vector+                   (Vector)+import qualified Data.Vector        as Vector+import           Prelude            hiding+                   (pi)++------------------------------------------------------------------------++type M = Matrix Double+type V = Vector Double++-- point-wise+(.*) :: M -> M -> M+(.*) = elementwise (*)+infixl 7 .*++(./) :: M -> M -> M+(./) = elementwise (/)+infixl 7 ./++(.-) :: M -> M -> M+(.-) = elementwise (-)+infixl 6 .-++-- scalar+(^*) :: Double -> M -> M+(^*) x = fmap (x *)+infixl 7 ^*++(^/) :: Double -> M -> M+(^/) x = fmap (x /)+infixl 7 ^/++norm_1 :: M -> Double+norm_1 = sum . toList++norm_F :: M -> Double+norm_F = sqrt . sum . toList . fmap (^(2 :: Int))++{- $setup+   >>> :set -XFlexibleContexts+   >>> :{+   let+     p :: M+     p = fromList 5 5+       [ 0, 1,    0,   0,    0+       , 0, 0,    0.5, 0.5,  0+       , 0, 0,    0.5, 0.25, 0.25+       , 0, 0.25, 0,   0,    0.75+       , 1, 0,    0,   0,    0+       ]+:}+-}++------------------------------------------------------------------------+-- * Number of occurrences of a state in a test case++-- | The fundamental matrix for absorbing chains. Its (i, j)-th entry is+-- the expected number of occurrences of state j prior to absorption at+-- the sink, given that one starts in state i. So the first row+-- indicates the expected occurence of each state starting from the+-- start state.+--+-- >>> fundamental (minorMatrix 5 5 p :: M)+-- ┌                                                                                 ┐+-- │                 1.0  1.2307692307692306  1.2307692307692308  0.9230769230769231 │+-- │                 0.0  1.2307692307692306  1.2307692307692308  0.9230769230769231 │+-- │                 0.0 0.15384615384615385  2.1538461538461537  0.6153846153846154 │+-- │                 0.0  0.3076923076923077  0.3076923076923077  1.2307692307692308 │+-- └                                                                                 ┘+fundamental+  :: M   -- ^ Reduced matrix (n x n).+  -> M   -- ^ n x n+fundamental m = case inverse (identity (nrows m) .- m) of+  Left err -> error $ "fundamental: " ++ err+  Right r  -> r++-- | Expected variance of the occurrence for each state. The (i, j)-th+-- entry should be read in the same away as that of the fundamental+-- matrix above.+--+-- >>> occVariance (fundamental (minorMatrix 5 5 p :: M))+-- ┌                                                                                 ┐+-- │                 0.0 0.28402366863905315  2.5562130177514795  0.4970414201183433 │+-- │                 0.0 0.28402366863905315  2.5562130177514795  0.4970414201183433 │+-- │                 0.0 0.20118343195266267  2.4852071005917153  0.5207100591715977 │+-- │                 0.0  0.3550295857988165  0.9230769230769231  0.2840236686390534 │+-- └                                                                                 ┘+occVariance+  :: M -- ^ Reduced matrix (n x n).+  -> M -- n x n+occVariance n = n * (2 ^* diagonal 0 (getDiag n) - identity dim) - (n .* n)+  where+    dim = nrows n++------------------------------------------------------------------------+-- * Computing the long-run state probabilities++-- | The Perron eigenvector (long-run occupancies/probabilities of states).+--+-- >>> perron p+-- [0.1857142857142857,0.22857142857142854,0.22857142857142856,0.17142857142857143,0.1857142857142857]+perron :: M -- ^ Transition matrix (n x n).+       -> V+perron p = Vector.map (/ l) n1+  where+    dim = nrows p++    n1 :: V+    n1 = getRow 1 (fundamental (minorMatrix dim dim p)) `Vector.snoc` 1++    l :: Double+    l  = getElem 1 1 $ rowVector n1 * colVector (getDiag (identity dim))++------------------------------------------------------------------------+-- * Sensitivity analysis++-- XXX: Algorithm on p. 31 in report.+_sensitivities :: M -> M+_sensitivities = undefined++------------------------------------------------------------------------+-- * Other long run statistics++-- | Compute the stimulus long-run occupancy.++{- | >>> :{+let s :: M+    s = fromList 4 5+      [ 1,    0,    0,    0,    0+      , 0,    0.5,  0.5,  0,    0+      , 0,    0.5,  0.25, 0.25, 0+      , 0.25, 0,    0,    0.5,  0.25+      ]+in sigma s (perron p)+:}+[0.2807017543859649,0.2807017543859649,0.21052631578947367,0.17543859649122806,5.2631578947368425e-2]+-}+sigma :: M -- ^ Stimulus matrix (n-1 x n).+      -> V -- ^ Perron eigenvector.+      -> V+sigma stimulus pi = Vector.fromList+  [ 1 / (1 - pi Vector.! (pred n))+    * sum [ pi Vector.! (pred i) * getElem i k stimulus+          | i <- [1 .. (pred n)]+          ]+  | k <- [1..n]+  ]+  where+    n = Vector.length pi++-- ------------------------------------------------------------------------+-- -- * Probability of occurrence for states++-- | Compute the probability of occurrence for each state.+--+-- >>> getRow 1 (nodeProbabilities p)+-- [1.0,1.0,0.5714285714285715,0.75]+nodeProbabilities+  :: M -- ^ Transition matrix (n x n).+  -> M -- n-1 x n-1+nodeProbabilities p = n * (diagonal 0 (getDiag (1 ^/ n)))+  where+    n = fundamental (minorMatrix dim dim p)+    dim = nrows p++-- (Algorithm 9 on p. 34.)++-- ------------------------------------------------------------------------++-- | Expected test case length.+--+-- >>> expectedLength (fundamental (minorMatrix 5 5 p :: M))+-- 4.384615384615385+expectedLength+  :: M -- ^ Fundamental matrix (n x n).+  -> Double+expectedLength = norm_1 . rowVector . (getRow 1)++-- ------------------------------------------------------------------------++-- | Success rate matrix.+--+-- >>> expectedArcReliability Nothing (fromList 2 2 [10,10,10,10], fromList 2 2 [0,1,2,3])+-- (┌                                       ┐+-- │ 0.9166666666666666 0.8461538461538461 │+-- │ 0.7857142857142857 0.7333333333333333 │+-- └                                       ┘,┌                                             ┐+-- │  5.876068376068376e-3  9.298393913778529e-3 │+-- │ 1.1224489795918367e-2 1.2222222222222223e-2 │+-- └                                             ┘)+-- >>> :{+--  expectedArcReliability+--    (Just (fromList 2 2 [10,10,10,10], fromList 2 2 [1,1,1,1]))+--    (fromList 2 2 [10,10,10,10], fromList 2 2 [0,1,2,3])+-- :}+-- (┌                                       ┐+-- │ 0.9130434782608695              0.875 │+-- │               0.84 0.8076923076923077 │+-- └                                       ┘,┌                                             ┐+-- │ 3.3081285444234404e-3              4.375e-3 │+-- │  5.169230769230769e-3  5.752794214332676e-3 │+-- └                                             ┘)+expectedArcReliability+  :: Maybe (M, M)+  -> (M, M)+  -> (M, M) -- ^ (mean, variance)+expectedArcReliability mprior (obsSuccs, obsFails) =+  ( alpha ./ (alpha + beta)+  , (alpha .* beta) ./ (ab .* ab .* (ab + ones))+  )+  where+    m = nrows obsSuccs+    n = ncols obsSuccs++    ones :: M+    ones = Matrix.fromList m n [1,1..]++    -- In case no prior information is available, a_{i,j} = b_{i,j} = 1.+    priorSuccs, priorFails :: M+    (priorSuccs, priorFails) =+      maybe (ones, ones) (bimap (+ ones) (+ ones)) mprior++    alpha, beta :: M+    alpha = priorSuccs + obsSuccs+    beta  = priorFails + obsFails++    ab :: M+    ab = alpha + beta++transientReliability+  :: M            -- ^ Reduced transition matrix (n-1 x n).+  -> Maybe (M, M) -- ^ Prior successes and failures (n-1 x n).+  -> (M, M)       -- ^ Observed successes and failures (n-1 x n).+  -> (M, M)       -- ^ Reliability vector and reliability variance vector.+transientReliability q mprior obs =+  (rstar, vstar)+  where+    n = ncols q++    (mean, var) = expectedArcReliability mprior obs++    r1 :: M+    r1 = mean++    fancyR :: M+    fancyR = q .* r1++    fancyRdot :: M -- n-1 x n-1+    fancyRdot = submatrix 1 (pred n) 1 (pred n) fancyR++    w :: M -- n-1 x 1+    w = colVector $ getCol n fancyR++    rstar :: M -- n-1 x 1+    rstar = fundamental fancyRdot * w++    r2 :: M+    r2 = r1 .* r1 + var++    fancyR' :: M -- n-1 x n-1+    fancyR' = q .* r2++    fancyRdot' :: M -- n-1 x n-1+    fancyRdot' = submatrix 1 (pred n) 1 (pred n) fancyR'++    w' :: M -- n-1 x 1+    w' = colVector $ getCol n fancyR'++    rstar' :: M -- n-1 x 1+    rstar' = fundamental fancyRdot' * w'++    vstar = rstar' - rstar .* rstar++singleUseReliability+  :: M            -- ^ Reduced transition matrix (n-1 x n).+  -> Maybe (M, M) -- ^ Prior successes and failures (n-1 x n).+  -> (M, M)       -- ^ Observed successes and failures (n-1 x n).+  -> (Double, Double)+singleUseReliability q mprior obs =+  bimap (head . toList) (head . toList) $ transientReliability q mprior obs++------------------------------------------------------------------------++{-+singleUseReliabilityIO :: (KnownNat n, KnownNat (n - 1))+  => (1 <= n - 1, n - 1 <= n, (n - (n - 1)) ~ 1)++  => L (n - 1) n                -- ^ Transition matrix without transitions to+                                -- the sink.++  -> FilePath                   -- ^ Filepath where prior successes are/will be+                                -- saved.++  -> FilePath                   -- ^ Filepath where prior failures are/will be+                                -- saved.++  -> (L (n - 1) n, L (n - 1) n) -- ^ Observed successes and failures.+  -> IO Double+singleUseReliabilityIO q fps fpf (s, f) = do+  mprior <- load2StaticMatrices fps fpf+  case mprior of+    Nothing -> do+      -- We need the elementwise @max 1@ below because otherwise we get division+      -- by zero in 'expectedArcReliability'. For details see the 2017 paper by by L. Lin,+      -- Y. Xue and F. Song in the README.+      saveStaticMatrix fps "%.1f" (dmmap (max 1) s)+      saveStaticMatrix fpf "%.1f" (dmmap (max 1) f)+    Just (ps, pf) -> do+      saveStaticMatrix fps "%.1f" (ps + s)+      saveStaticMatrix fpf "%.1f" (pf + f)+  return (singleUseReliability q mprior (s, f))++loadStaticMatrix :: (KnownNat m, KnownNat n) => FilePath -> IO (Maybe (L m n))+loadStaticMatrix = fmap (join . fmap create) . D.loadMatrix'++load2StaticMatrices :: (KnownNat m, KnownNat n, KnownNat o, KnownNat p)+                    => FilePath -> FilePath -> IO (Maybe (L m n, L o p))+load2StaticMatrices fp1 fp2 = liftMA2 (loadStaticMatrix fp1) (loadStaticMatrix fp2)+  where+    liftMA2 :: (Monad m, Applicative f) => m (f a) -> m (f b) -> m (f (a, b))+    liftMA2 = liftM2 (liftA2 (,))++saveStaticMatrix :: (KnownNat m, KnownNat n)+                 => FilePath+                 -> String   -- ^ "printf" format (e.g. "%.2f", "%g", etc.)+                 -> L m n -> IO ()+saveStaticMatrix fp format = D.saveMatrix fp format . unwrap+-}++------------------------------------------------------------------------++-- | Kullback-Leibler matrix discrimination.++{- | >>> :{+let+  s = fromList 4 5+        [ 1,    0,   0,    0,    0+        , 0,    0.5, 0.5,  0,    0+        , 0,    0.5, 0.25, 0.25, 0+        , 0.25, 0,   0,    0.5,  0.25+        ]+  es = Vector.fromList [4, 5, 7, 3, 4]+  et = fromList 4 5+    [ 4, 0, 0, 0, 0+    , 0, 3, 2, 0, 0+    , 0, 4, 1, 2, 0+    , 1, 0, 0, 1, 1+    ]+in kullbackLeibler p s es et+:}+3.440540391434413e-2+-}+kullbackLeibler+  :: M      -- ^ Transitions (n x n).+  -> M      -- ^ Stimulis (m x n).+  -> V      -- ^ State visitations (n).+  -> M      -- ^ Stimulus execution (m x n).+  -> Double -- ^ Discrimination.+kullbackLeibler p s es et = Vector.sum k'+  where+    m = nrows s+    n = ncols p++    pi' :: V+    pi' = Vector.take m (perron p)++    es' :: V+    es' = Vector.take m es++    es'' :: M+    es'' = let v = colVector es'+           in List.foldl' (<|>) v (take (pred n) (repeat v))++    t :: M+    t = es'' ./ et++    k :: V+    k  = Vector.fromList $ map sum $ toLists $ (1 / log 2) ^* (s .* fmap log' (s .* t))++    k' :: V+    k' = Vector.zipWith (*) pi' k++    log' :: Double -> Double+    log' x+      | x == 0    = 0+      | isNaN x   = 0+      | otherwise = log x
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF tasty-discover -optF --tree-display #-}
+ test/Unit.hs view
@@ -0,0 +1,118 @@+module Unit+  ( unit_expectExpectedArcReliability+  , unit_expectTransientReliability+  , unit_occurenceMean+  , unit_occurenceVar+  , unit_docTest+  ) where++import           Data.Matrix+import qualified Data.Vector      as Vector+import           Prelude          hiding+                   (pi)+import           Test.DocTest+                   (doctest)+import           Test.Tasty.HUnit+                   (Assertion, (@?), (@?=))++import           MarkovChain++------------------------------------------------------------------------++-- cf. A Simpler and More Direct Derivation of System Reliability Using Markov Chain Usage Models (2017)+-- by Lan Lin, Yufeng Xue and Fengguang Song++-- Transient usage model (transitions from sink omitted).+q :: M+q = fromList 2 3+  [ 0, 0.5, 0.5+  , 0, 0.5, 0.5+  ]++successes :: M+successes = fromList 2 3+  [ 1, 2, 3+  , 4, 5, 6+  ]++failures :: M+failures = fromList 2 3+  [ 1, 0, 1+  , 0, 1, 0+  ]++-- Observed transient success rate.+sr :: M+(sr, _) = expectedArcReliability Nothing (successes, failures)++unit_expectExpectedArcReliability :: Assertion+unit_expectExpectedArcReliability = sr @?= expected+  where+    expected :: M+    expected = fromList 2 3+      [ 2/4, 3/4, 4/6+      , 5/6, 6/8, 7/8+      ]++-- Transient reliability matrix.+tr :: M+tr = fst $ transientReliability q Nothing (successes, failures)++unit_expectTransientReliability :: Assertion+unit_expectTransientReliability =+  (norm_F (tr - expected)) <= 1.0e-6 @? "differs from expected"+  where+    a = (4/6)/2+    b = (3/4)*(7/8)/4+    c = (7/8)/2+    d = 1 - (6/8)/2++    expected :: M+    expected = fromList 2 1+      [ a + b/d+      , c/d+      ]++------------------------------------------------------------------------++-- cf. Computations for Markov Chain Usage Models (2000)+-- by S. J. Prowell++-- Usage model.+p :: M+p = fromList 5 5+  [ 0, 1,    0,   0,    0+  , 0, 0,    0.5, 0.5,  0+  , 0, 0,    0.5, 0.25, 0.25+  , 0, 0.25, 0,   0,    0.75+  , 1, 0,    0,   0,    0+  ]++q' :: M+q' = minorMatrix 5 5 p++-- State occurences.+unit_occurenceMean :: Assertion+unit_occurenceMean =+  norm_F (rowVector actual .- rowVector expected) <= 1.0e-3 @? "differs from expected"+  where+    actual :: V+    actual = getRow 1 (fundamental q')++    expected :: V+    expected = Vector.fromList+      [ 1.0, 1.231, 1.231, 0.9231 ]++unit_occurenceVar :: Assertion+unit_occurenceVar =+  norm_F (rowVector actual .- rowVector expected) <= 1.0e-3 @? "differs from expected"+  where+    actual :: V+    actual = getRow 1 (occVariance (fundamental q'))++    expected :: V+    expected = Vector.fromList+      [ 0, 0.284, 2.556, 0.497 ]++unit_docTest :: IO ()+unit_docTest = doctest ["src/MarkovChain.hs"]