packages feed

tsne 1.2.0 → 1.3.0

raw patch · 15 files changed

+435/−310 lines, 15 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Algorithm.TSNE.Internals: Beta :: Double -> Double -> Double -> Beta
- Data.Algorithm.TSNE.Internals: [betaMax] :: Beta -> Double
- Data.Algorithm.TSNE.Internals: [betaMin] :: Beta -> Double
- Data.Algorithm.TSNE.Internals: [betaValue] :: Beta -> Double
- Data.Algorithm.TSNE.Internals: binarySearchBeta :: TSNEOptions -> TSNEInput -> TSNEInputValue -> Beta
- Data.Algorithm.TSNE.Internals: binarySearchBeta' :: TSNEOptions -> TSNEInput -> Double -> Int -> Beta -> TSNEInputValue -> Beta
- Data.Algorithm.TSNE.Internals: cost :: [[Double]] -> TSNEState -> Double
- Data.Algorithm.TSNE.Internals: data Beta
- Data.Algorithm.TSNE.Internals: entropyForInputValue :: Double -> TSNEInput -> TSNEInputValue -> Entropy
- Data.Algorithm.TSNE.Internals: gradients :: [[Probability]] -> TSNEState -> [[Gradient]]
- Data.Algorithm.TSNE.Internals: initSolution2D :: Int -> IO [[Double]]
- Data.Algorithm.TSNE.Internals: initSolution3D :: Int -> IO [[Double]]
- Data.Algorithm.TSNE.Internals: initState2D :: Int -> IO TSNEState
- Data.Algorithm.TSNE.Internals: initState3D :: Int -> IO TSNEState
- Data.Algorithm.TSNE.Internals: inputIsValid :: TSNEInput -> Either String ()
- Data.Algorithm.TSNE.Internals: inputSize :: TSNEInput -> Int
- Data.Algorithm.TSNE.Internals: inputValueSize :: TSNEInput -> Int
- Data.Algorithm.TSNE.Internals: isValidStateForInput :: TSNEInput -> TSNEState -> Either String ()
- Data.Algorithm.TSNE.Internals: neighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]
- Data.Algorithm.TSNE.Internals: newDelta :: Double -> Int -> Gain -> Delta -> Gradient -> Delta
- Data.Algorithm.TSNE.Internals: newGain :: Gain -> Delta -> Gradient -> Gain
- Data.Algorithm.TSNE.Internals: output2D :: [[Double]] -> TSNEState -> TSNEOutput2D
- Data.Algorithm.TSNE.Internals: output3D :: [[Double]] -> TSNEState -> TSNEOutput3D
- Data.Algorithm.TSNE.Internals: rawNeighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]
- Data.Algorithm.TSNE.Internals: runTSNE2D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput2D IO ()
- Data.Algorithm.TSNE.Internals: runTSNE3D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput3D IO ()
- Data.Algorithm.TSNE.Internals: solution2D :: [[Double]] -> [Position2D]
- Data.Algorithm.TSNE.Internals: solution3D :: [[Double]] -> [Position3D]
- Data.Algorithm.TSNE.Internals: stepTSNE :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> TSNEState
- Data.Algorithm.TSNE.Internals: targetEntropy :: TSNEOptions -> Entropy
+ Data.Algorithm.TSNE.Checks: inputIsValid :: TSNEInput -> Either String ()
+ Data.Algorithm.TSNE.Checks: inputSize :: TSNEInput -> Int
+ Data.Algorithm.TSNE.Checks: inputValueSize :: TSNEInput -> Int
+ Data.Algorithm.TSNE.Checks: isValidStateForInput :: Int -> TSNEInput -> TSNEState -> Either String ()
+ Data.Algorithm.TSNE.Preparation: Beta :: Double -> Double -> Double -> Beta
+ Data.Algorithm.TSNE.Preparation: [betaMax] :: Beta -> Double
+ Data.Algorithm.TSNE.Preparation: [betaMin] :: Beta -> Double
+ Data.Algorithm.TSNE.Preparation: [betaValue] :: Beta -> Double
+ Data.Algorithm.TSNE.Preparation: binarySearchBeta :: TSNEOptions -> TSNEInput -> TSNEInputValue -> Beta
+ Data.Algorithm.TSNE.Preparation: binarySearchBeta' :: TSNEOptions -> TSNEInput -> Double -> Int -> Beta -> TSNEInputValue -> Beta
+ Data.Algorithm.TSNE.Preparation: data Beta
+ Data.Algorithm.TSNE.Preparation: entropyForInputValue :: Double -> TSNEInput -> TSNEInputValue -> Entropy
+ Data.Algorithm.TSNE.Preparation: neighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]
+ Data.Algorithm.TSNE.Preparation: rawNeighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]
+ Data.Algorithm.TSNE.Preparation: targetEntropy :: TSNEOptions -> Entropy
+ Data.Algorithm.TSNE.Run2D: initSolution2D :: Int -> IO [[Double]]
+ Data.Algorithm.TSNE.Run2D: initState2D :: Int -> IO TSNEState
+ Data.Algorithm.TSNE.Run2D: output2D :: [[Double]] -> TSNEState -> TSNEOutput2D
+ Data.Algorithm.TSNE.Run2D: runTSNE2D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput2D IO ()
+ Data.Algorithm.TSNE.Run2D: solution2D :: [[Double]] -> [Position2D]
+ Data.Algorithm.TSNE.Run3D: initSolution3D :: Int -> IO [[Double]]
+ Data.Algorithm.TSNE.Run3D: initState3D :: Int -> IO TSNEState
+ Data.Algorithm.TSNE.Run3D: output3D :: [[Double]] -> TSNEState -> TSNEOutput3D
+ Data.Algorithm.TSNE.Run3D: runTSNE3D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput3D IO ()
+ Data.Algorithm.TSNE.Run3D: solution3D :: [[Double]] -> [Position3D]
+ Data.Algorithm.TSNE.Stepping: cost :: [[Double]] -> TSNEState -> Double
+ Data.Algorithm.TSNE.Stepping: gradients :: [[Probability]] -> TSNEState -> [[Gradient]]
+ Data.Algorithm.TSNE.Stepping: newDelta :: Double -> Int -> Gain -> Delta -> Gradient -> Delta
+ Data.Algorithm.TSNE.Stepping: newGain :: Gain -> Delta -> Gradient -> Gain
+ Data.Algorithm.TSNE.Stepping: stepTSNE :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> TSNEState

Files

src/Data/Algorithm/TSNE.hs view
@@ -11,8 +11,11 @@ import Pipes  import Data.Algorithm.TSNE.Types-import Data.Algorithm.TSNE.Internals import Data.Algorithm.TSNE.Utils+import Data.Algorithm.TSNE.Preparation+import Data.Algorithm.TSNE.Run3D+import Data.Algorithm.TSNE.Run2D+  -- | Generates an infinite stream of 3D tSNE iterations. tsne3D :: TSNEOptions -> TSNEInput -> Producer TSNEOutput3D IO ()
src/Data/Algorithm/TSNE/Checks.hs view
@@ -17,3 +17,24 @@ isRectangular :: [[a]] -> Bool isRectangular xss = has2DShape (shape2D xss) xss +inputSize :: TSNEInput -> Int+inputSize = length++inputValueSize :: TSNEInput -> Int+inputValueSize i = w +    where (w,h) = shape2D i ++inputIsValid :: TSNEInput -> Either String ()+inputIsValid [] = Left "empty input data"+inputIsValid xss+    | not (isRectangular xss) = Left "input data values are not all the same length"+    | otherwise = Right () ++isValidStateForInput :: Int -> TSNEInput -> TSNEState -> Either String ()+isValidStateForInput d i st+    | not (has2DShape (n,d) s) = Left $ "solution is wrong shape: " ++ show (shape2D s) +    | otherwise = Right ()+        where+            n = inputSize i+            s = stSolution st  +
− src/Data/Algorithm/TSNE/Internals.hs
@@ -1,207 +0,0 @@-module Data.Algorithm.TSNE.Internals where--import Control.Applicative-import Control.DeepSeq-import Control.Exception (assert)-import Data.Default (def)-import Data.List(zipWith4)-import Data.Random.Normal (normalsIO')-import Pipes---import Debug.Trace--import Data.Algorithm.TSNE.Types-import Data.Algorithm.TSNE.Utils-import Data.Algorithm.TSNE.Checks---inputSize :: TSNEInput -> Int-inputSize = length--inputValueSize :: TSNEInput -> Int-inputValueSize i = w -    where (w,h) = shape2D i --inputIsValid :: TSNEInput -> Either String ()-inputIsValid [] = Left "empty input data"-inputIsValid xss-    | not (isRectangular xss) = Left "input data values are not all the same length"-    | otherwise = Right () --isValidStateForInput :: TSNEInput -> TSNEState -> Either String ()-isValidStateForInput i st-    | not (has2DShape (n,3) s) = Left $ "solution is wrong shape: " ++ show (shape2D s) -    | otherwise = Right ()-        where-            n = inputSize i-            s = stSolution st    --initState3D :: Int -> IO TSNEState-initState3D n = do-    s <- initSolution3D n-    return $ TSNEState 0 s (rr 1) (rr 0)-        where-            rr = repeat.repeat--initState2D :: Int -> IO TSNEState-initState2D n = do-    s <- initSolution2D n-    return $ TSNEState 0 s (rr 1) (rr 0)-        where-            rr = repeat.repeat--initSolution3D :: Int -> IO [[Double]]-initSolution3D n = do-    let ns = normalsIO' (0, 1e-4)-    xs <- ns-    ys <- ns-    zs <- ns-    return $ take n <$> [xs,ys,zs]--initSolution2D :: Int -> IO [[Double]]-initSolution2D n = do-    let ns = normalsIO' (0, 1e-4)-    xs <- ns-    ys <- ns-    return $ take n <$> [xs,ys]--runTSNE3D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput3D IO ()-runTSNE3D opts vs ps st = do-    yield $ output3D ps st-    let st' = force $ stepTSNE opts vs ps st-    runTSNE3D opts vs ps st'--runTSNE2D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput2D IO ()-runTSNE2D opts vs ps st = do-    yield $ output2D ps st-    let st' = force $ stepTSNE opts vs ps st-    runTSNE2D opts vs ps st'--stepTSNE :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> TSNEState-stepTSNE opts vs ps st = TSNEState i' s'' g' d'-    where-        i = stIteration st-        s = stSolution st-        g = stGains st-        d = stDeltas st-        gr = gradients ps st-        i' = i + 1-        s' = recenter $ z (+) s d'-        g' = z3 newGain g d gr-        d' = z3 (newDelta (tsneLearningRate opts) i) g' d gr-        z = zipWith.zipWith-        z3 = zipWith3.zipWith3-        s'' = assert (length s' == length vs) s'--newGain :: Gain -> Delta -> Gradient -> Gain-newGain g d gr = max 0.01 g'-    where-        g' = if signum d == signum gr -                then g * 0.8-                else g + 0.2  --newDelta :: Double -> Int -> Gain -> Delta -> Gradient -> Delta-newDelta e i g' d gr = (m * d) - (e * g' * gr)-    where-        m = if i < 250 then 0.5 else 0.8--gradients :: [[Probability]] -> TSNEState -> [[Gradient]]-gradients pss st = gradient <$> ss-    where-        gradient :: [Double] -> [Gradient]-        gradient s = zipWith4 (f s) s pss qss qss'-        ss = stSolution st-        i = stIteration st-        qss = qdist ss-        qss' = qdist' ss -        f :: [Double] -> Double -> [Double] -> [Double] -> [Double] -> Gradient-        f s x ps qs qs' = sum $ zipWith4 g s ps qs qs'-            where-                g y p q q' = m * (x - y)-                    where-                        m = 4 * (k * p - q') * q-                        k = if i < 100 then 4 else 1--solution3D :: [[Double]] -> [Position3D]-solution3D (xs:ys:zs:_) = zip3 xs ys zs--output3D :: [[Double]] -> TSNEState -> TSNEOutput3D-output3D pss st = TSNEOutput3D i s c-    where-        i = stIteration st-        s = (solution3D . stSolution) st-        c = cost pss st--solution2D :: [[Double]] -> [Position2D]-solution2D (xs:ys:_) = zip xs ys--output2D :: [[Double]] -> TSNEState -> TSNEOutput2D-output2D pss st = TSNEOutput2D i s c-    where-        i = stIteration st-        s = (solution2D . stSolution) st-        c = cost pss st--cost :: [[Double]] -> TSNEState -> Double-cost pss st = sumsum $ (zipWith.zipWith) c pss (qdist' (stSolution st))-    where-        c p q = -p * log q --targetEntropy :: TSNEOptions -> Entropy-targetEntropy = log.realToFrac.tsnePerplexity--data Beta = Beta {-    betaValue :: Double,-    betaMin :: Double,-    betaMax :: Double-}--neighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]-neighbourProbabilities opts vs = symmetrize $ rawNeighbourProbabilities opts vs--rawNeighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]-rawNeighbourProbabilities opts vs = map np vs-    where -        np a = aps (beta a) vs a-        beta a = betaValue $ binarySearchBeta opts vs a--        aps :: Double -> TSNEInput -> TSNEInputValue -> [Probability]-        aps beta bs a = map pj' bs-            where-                psum = sum $ map pj bs-                pj b -                    | a == b    = 0-                    | otherwise = exp $ -(distanceSquared a b) * beta -                pj' b = pj b / psum--binarySearchBeta :: TSNEOptions -> TSNEInput -> TSNEInputValue -> Beta-binarySearchBeta opts vs = binarySearchBeta' opts vs 1e-4 0 (Beta 1 (-infinity) infinity)--binarySearchBeta' :: TSNEOptions -> TSNEInput -> Double -> Int -> Beta -> TSNEInputValue -> Beta-binarySearchBeta' opts bs tol i beta a-    | i == 50            = beta-    | abs (e - t) < tol  = beta-    | e > t              = r $ incPrecision beta-    | otherwise          = r $ decPrecision beta -        where-            t = targetEntropy opts-            e = entropyForInputValue (betaValue beta) bs a-            incPrecision (Beta b _ bmax) -                | bmax == infinity = Beta (b * 2) b bmax-                | otherwise        = Beta ((b + bmax) / 2) b bmax-            decPrecision (Beta b bmin _) -                | bmin == -infinity = Beta (b / 2) bmin b-                | otherwise         = Beta ((b + bmin) / 2) bmin b-            r beta' = binarySearchBeta' opts bs tol (i+1) beta' a --entropyForInputValue :: Double -> TSNEInput -> TSNEInputValue -> Entropy-entropyForInputValue beta bs a = sum $ map h bs-    where-        h b = if x > 1e-7 then -x * log x else 0-            where x = pj' b-        psum = sum $ map pj bs-        pj b -            | a == b    = 0-            | otherwise = exp $ -(distanceSquared a b) * beta -        pj' b = pj b / psum--
+ src/Data/Algorithm/TSNE/Preparation.hs view
@@ -0,0 +1,65 @@+module Data.Algorithm.TSNE.Preparation where++import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Utils+++targetEntropy :: TSNEOptions -> Entropy+targetEntropy = log.realToFrac.tsnePerplexity++data Beta = Beta {+    betaValue :: Double,+    betaMin :: Double,+    betaMax :: Double+}++neighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]+neighbourProbabilities opts vs = symmetrize $ rawNeighbourProbabilities opts vs++rawNeighbourProbabilities :: TSNEOptions -> TSNEInput -> [[Probability]]+rawNeighbourProbabilities opts vs = map np vs+    where +        np a = aps (beta a) vs a+        beta a = betaValue $ binarySearchBeta opts vs a++        aps :: Double -> TSNEInput -> TSNEInputValue -> [Probability]+        aps beta bs a = map pj' bs+            where+                psum = sum $ map pj bs+                pj b +                    | a == b    = 0+                    | otherwise = exp $ -(distanceSquared a b) * beta +                pj' b = pj b / psum++binarySearchBeta :: TSNEOptions -> TSNEInput -> TSNEInputValue -> Beta+binarySearchBeta opts vs = binarySearchBeta' opts vs 1e-4 0 (Beta 1 (-infinity) infinity)++binarySearchBeta' :: TSNEOptions -> TSNEInput -> Double -> Int -> Beta -> TSNEInputValue -> Beta+binarySearchBeta' opts bs tol i beta a+    | i == 50            = beta+    | abs (e - t) < tol  = beta+    | e > t              = r $ incPrecision beta+    | otherwise          = r $ decPrecision beta +        where+            t = targetEntropy opts+            e = entropyForInputValue (betaValue beta) bs a+            incPrecision (Beta b _ bmax) +                | bmax == infinity = Beta (b * 2) b bmax+                | otherwise        = Beta ((b + bmax) / 2) b bmax+            decPrecision (Beta b bmin _) +                | bmin == -infinity = Beta (b / 2) bmin b+                | otherwise         = Beta ((b + bmin) / 2) bmin b+            r beta' = binarySearchBeta' opts bs tol (i+1) beta' a ++entropyForInputValue :: Double -> TSNEInput -> TSNEInputValue -> Entropy+entropyForInputValue beta bs a = sum $ map h bs+    where+        h b = if x > 1e-7 then -x * log x else 0+            where x = pj' b+        psum = sum $ map pj bs+        pj b +            | a == b    = 0+            | otherwise = exp $ -(distanceSquared a b) * beta +        pj' b = pj b / psum++
+ src/Data/Algorithm/TSNE/Run2D.hs view
@@ -0,0 +1,42 @@+module Data.Algorithm.TSNE.Run2D where++import Control.Applicative+import Control.DeepSeq+import Data.Random.Normal (normalsIO')+import Pipes++import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Preparation+import Data.Algorithm.TSNE.Stepping+++initState2D :: Int -> IO TSNEState+initState2D n = do+    s <- initSolution2D n+    return $ TSNEState 0 s (rr 1) (rr 0)+        where+            rr = repeat.repeat++initSolution2D :: Int -> IO [[Double]]+initSolution2D n = do+    let ns = normalsIO' (0, 1e-4)+    xs <- ns+    ys <- ns+    return $ take n <$> [xs,ys]++runTSNE2D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput2D IO ()+runTSNE2D opts vs ps st = do+    yield $ output2D ps st+    let st' = force $ stepTSNE opts vs ps st+    runTSNE2D opts vs ps st'++solution2D :: [[Double]] -> [Position2D]+solution2D (xs:ys:_) = zip xs ys++output2D :: [[Double]] -> TSNEState -> TSNEOutput2D+output2D pss st = TSNEOutput2D i s c+    where+        i = stIteration st+        s = (solution2D . stSolution) st+        c = cost pss st+
+ src/Data/Algorithm/TSNE/Run3D.hs view
@@ -0,0 +1,43 @@+module Data.Algorithm.TSNE.Run3D where++import Control.Applicative+import Control.DeepSeq+import Data.Random.Normal (normalsIO')+import Pipes++import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Preparation+import Data.Algorithm.TSNE.Stepping+++initState3D :: Int -> IO TSNEState+initState3D n = do+    s <- initSolution3D n+    return $ TSNEState 0 s (rr 1) (rr 0)+        where+            rr = repeat.repeat++initSolution3D :: Int -> IO [[Double]]+initSolution3D n = do+    let ns = normalsIO' (0, 1e-4)+    xs <- ns+    ys <- ns+    zs <- ns+    return $ take n <$> [xs,ys,zs]++runTSNE3D :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput3D IO ()+runTSNE3D opts vs ps st = do+    yield $ output3D ps st+    let st' = force $ stepTSNE opts vs ps st+    runTSNE3D opts vs ps st'++solution3D :: [[Double]] -> [Position3D]+solution3D (xs:ys:zs:_) = zip3 xs ys zs++output3D :: [[Double]] -> TSNEState -> TSNEOutput3D+output3D pss st = TSNEOutput3D i s c+    where+        i = stIteration st+        s = (solution3D . stSolution) st+        c = cost pss st+
+ src/Data/Algorithm/TSNE/Stepping.hs view
@@ -0,0 +1,60 @@+module Data.Algorithm.TSNE.Stepping where++import Control.Applicative+import Control.Exception (assert)+import Data.List(zipWith4)++import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Utils+++stepTSNE :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> TSNEState+stepTSNE opts vs ps st = TSNEState i' s'' g' d'+    where+        i = stIteration st+        s = stSolution st+        g = stGains st+        d = stDeltas st+        gr = gradients ps st+        i' = i + 1+        s' = recenter $ z (+) s d'+        g' = z3 newGain g d gr+        d' = z3 (newDelta (tsneLearningRate opts) i) g' d gr+        z = zipWith.zipWith+        z3 = zipWith3.zipWith3+        s'' = assert (length s' == length vs) s'++newGain :: Gain -> Delta -> Gradient -> Gain+newGain g d gr = max 0.01 g'+    where+        g' = if signum d == signum gr +                then g * 0.8+                else g + 0.2  ++newDelta :: Double -> Int -> Gain -> Delta -> Gradient -> Delta+newDelta e i g' d gr = (m * d) - (e * g' * gr)+    where+        m = if i < 250 then 0.5 else 0.8++gradients :: [[Probability]] -> TSNEState -> [[Gradient]]+gradients pss st = gradient <$> ss+    where+        gradient :: [Double] -> [Gradient]+        gradient s = zipWith4 (f s) s pss qss qss'+        ss = stSolution st+        i = stIteration st+        qss = qdist ss+        qss' = qdist' ss +        f :: [Double] -> Double -> [Double] -> [Double] -> [Double] -> Gradient+        f s x ps qs qs' = sum $ zipWith4 g s ps qs qs'+            where+                g y p q q' = m * (x - y)+                    where+                        m = 4 * (k * p - q') * q+                        k = if i < 100 then 4 else 1++cost :: [[Double]] -> TSNEState -> Double+cost pss st = sumsum $ (zipWith.zipWith) c pss (qdist' (stSolution st))+    where+        c p q = -p * log q +
− test/Data/Algorithm/TSNE/InternalsSpec.hs
@@ -1,97 +0,0 @@-module Data.Algorithm.TSNE.InternalsSpec (main, spec) where--import Data.Default (def)--import Test.Hspec-import Data.Algorithm.TSNE.Internals-import Data.Algorithm.TSNE.Checks-import Data.Algorithm.TSNE.Types-import Data.Algorithm.TSNE.Utils---- `main` is here so that this module can be run from GHCi on its own.  It is--- not needed for automatic spec discovery.-main :: IO ()-main = hspec spec--u = undefined--spec :: Spec-spec = do-    let n = inputSize testInput-        w = inputValueSize testInput--    describe "testInput" $ do-        it "is right shape" $ do-            testInput `shouldSatisfy` has2DShape (64, 20)-        it "is valid" $ do-            inputIsValid testInput `shouldBe` Right ()-        it "has right size" $ do-            inputSize testInput `shouldBe` 20-        it "has right value size" $ do-            inputValueSize testInput `shouldBe` 64--    describe "initSolution3D" $ do-        it "is right shape" $-            --initSolution3D 99 >>= (`shouldSatisfy` (\s -> length s == 3 && all (\xs -> length xs == 99) s))-            initSolution3D n >>= (`shouldSatisfy` has2DShape (n,3))--    describe "initState" $ do-        it "is valid state" $-            initState n >>= (`shouldSatisfy` isRight . (isValidStateForInput testInput))--    describe "neighbourProbabilities" $ do-        it "is right shape" $ do-            testNeighbourProbs `shouldSatisfy` has2DShape (n,n)--    describe "qdist" $ do-        it "is right shape" $ do-            s <- initSolution3D n-            qdist s `shouldSatisfy` has2DShape (n,n)        --    describe "qdist'" $ do-        it "is right shape" $ do-            s <- initSolution3D n-            qdist' s `shouldSatisfy` has2DShape (n,n)        --    describe "gradients" $ do-        it "is right shape" $ do-            s <- initState n-            gradients testNeighbourProbs s `shouldSatisfy` has2DShape (n,3)--    describe "stepTSNE" $ do-        it "works" $ do -            s <- initState n-            stepTSNE def testInput testNeighbourProbs s `shouldSatisfy` isRight . (isValidStateForInput testInput)---isRight :: Either a b -> Bool-isRight (Left _) = False-isRight (Right _) = True---- first 20 digits from the Python sklearn digits dataset-testInput :: TSNEInput-testInput = [-                [0.0,0.0,5.0,13.0,9.0,1.0,0.0,0.0,0.0,0.0,13.0,15.0,10.0,15.0,5.0,0.0,0.0,3.0,15.0,2.0,0.0,11.0,8.0,0.0,0.0,4.0,12.0,0.0,0.0,8.0,8.0,0.0,0.0,5.0,8.0,0.0,0.0,9.0,8.0,0.0,0.0,4.0,11.0,0.0,1.0,12.0,7.0,0.0,0.0,2.0,14.0,5.0,10.0,12.0,0.0,0.0,0.0,0.0,6.0,13.0,10.0,0.0,0.0,0.0],-                [0.0,0.0,0.0,12.0,13.0,5.0,0.0,0.0,0.0,0.0,0.0,11.0,16.0,9.0,0.0,0.0,0.0,0.0,3.0,15.0,16.0,6.0,0.0,0.0,0.0,7.0,15.0,16.0,16.0,2.0,0.0,0.0,0.0,0.0,1.0,16.0,16.0,3.0,0.0,0.0,0.0,0.0,1.0,16.0,16.0,6.0,0.0,0.0,0.0,0.0,1.0,16.0,16.0,6.0,0.0,0.0,0.0,0.0,0.0,11.0,16.0,10.0,0.0,0.0],-                [0.0,0.0,0.0,4.0,15.0,12.0,0.0,0.0,0.0,0.0,3.0,16.0,15.0,14.0,0.0,0.0,0.0,0.0,8.0,13.0,8.0,16.0,0.0,0.0,0.0,0.0,1.0,6.0,15.0,11.0,0.0,0.0,0.0,1.0,8.0,13.0,15.0,1.0,0.0,0.0,0.0,9.0,16.0,16.0,5.0,0.0,0.0,0.0,0.0,3.0,13.0,16.0,16.0,11.0,5.0,0.0,0.0,0.0,0.0,3.0,11.0,16.0,9.0,0.0],-                [0.0,0.0,7.0,15.0,13.0,1.0,0.0,0.0,0.0,8.0,13.0,6.0,15.0,4.0,0.0,0.0,0.0,2.0,1.0,13.0,13.0,0.0,0.0,0.0,0.0,0.0,2.0,15.0,11.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,12.0,12.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,10.0,8.0,0.0,0.0,0.0,8.0,4.0,5.0,14.0,9.0,0.0,0.0,0.0,7.0,13.0,13.0,9.0,0.0,0.0],-                [0.0,0.0,0.0,1.0,11.0,0.0,0.0,0.0,0.0,0.0,0.0,7.0,8.0,0.0,0.0,0.0,0.0,0.0,1.0,13.0,6.0,2.0,2.0,0.0,0.0,0.0,7.0,15.0,0.0,9.0,8.0,0.0,0.0,5.0,16.0,10.0,0.0,16.0,6.0,0.0,0.0,4.0,15.0,16.0,13.0,16.0,1.0,0.0,0.0,0.0,0.0,3.0,15.0,10.0,0.0,0.0,0.0,0.0,0.0,2.0,16.0,4.0,0.0,0.0],-                [0.0,0.0,12.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,14.0,16.0,16.0,14.0,0.0,0.0,0.0,0.0,13.0,16.0,15.0,10.0,1.0,0.0,0.0,0.0,11.0,16.0,16.0,7.0,0.0,0.0,0.0,0.0,0.0,4.0,7.0,16.0,7.0,0.0,0.0,0.0,0.0,0.0,4.0,16.0,9.0,0.0,0.0,0.0,5.0,4.0,12.0,16.0,4.0,0.0,0.0,0.0,9.0,16.0,16.0,10.0,0.0,0.0],-                [0.0,0.0,0.0,12.0,13.0,0.0,0.0,0.0,0.0,0.0,5.0,16.0,8.0,0.0,0.0,0.0,0.0,0.0,13.0,16.0,3.0,0.0,0.0,0.0,0.0,0.0,14.0,13.0,0.0,0.0,0.0,0.0,0.0,0.0,15.0,12.0,7.0,2.0,0.0,0.0,0.0,0.0,13.0,16.0,13.0,16.0,3.0,0.0,0.0,0.0,7.0,16.0,11.0,15.0,8.0,0.0,0.0,0.0,1.0,9.0,15.0,11.0,3.0,0.0],-                [0.0,0.0,7.0,8.0,13.0,16.0,15.0,1.0,0.0,0.0,7.0,7.0,4.0,11.0,12.0,0.0,0.0,0.0,0.0,0.0,8.0,13.0,1.0,0.0,0.0,4.0,8.0,8.0,15.0,15.0,6.0,0.0,0.0,2.0,11.0,15.0,15.0,4.0,0.0,0.0,0.0,0.0,0.0,16.0,5.0,0.0,0.0,0.0,0.0,0.0,9.0,15.0,1.0,0.0,0.0,0.0,0.0,0.0,13.0,5.0,0.0,0.0,0.0,0.0],-                [0.0,0.0,9.0,14.0,8.0,1.0,0.0,0.0,0.0,0.0,12.0,14.0,14.0,12.0,0.0,0.0,0.0,0.0,9.0,10.0,0.0,15.0,4.0,0.0,0.0,0.0,3.0,16.0,12.0,14.0,2.0,0.0,0.0,0.0,4.0,16.0,16.0,2.0,0.0,0.0,0.0,3.0,16.0,8.0,10.0,13.0,2.0,0.0,0.0,1.0,15.0,1.0,3.0,16.0,8.0,0.0,0.0,0.0,11.0,16.0,15.0,11.0,1.0,0.0],-                [0.0,0.0,11.0,12.0,0.0,0.0,0.0,0.0,0.0,2.0,16.0,16.0,16.0,13.0,0.0,0.0,0.0,3.0,16.0,12.0,10.0,14.0,0.0,0.0,0.0,1.0,16.0,1.0,12.0,15.0,0.0,0.0,0.0,0.0,13.0,16.0,9.0,15.0,2.0,0.0,0.0,0.0,0.0,3.0,0.0,9.0,11.0,0.0,0.0,0.0,0.0,0.0,9.0,15.0,4.0,0.0,0.0,0.0,9.0,12.0,13.0,3.0,0.0,0.0],-                [0.0,0.0,1.0,9.0,15.0,11.0,0.0,0.0,0.0,0.0,11.0,16.0,8.0,14.0,6.0,0.0,0.0,2.0,16.0,10.0,0.0,9.0,9.0,0.0,0.0,1.0,16.0,4.0,0.0,8.0,8.0,0.0,0.0,4.0,16.0,4.0,0.0,8.0,8.0,0.0,0.0,1.0,16.0,5.0,1.0,11.0,3.0,0.0,0.0,0.0,12.0,12.0,10.0,10.0,0.0,0.0,0.0,0.0,1.0,10.0,13.0,3.0,0.0,0.0],-                [0.0,0.0,0.0,0.0,14.0,13.0,1.0,0.0,0.0,0.0,0.0,5.0,16.0,16.0,2.0,0.0,0.0,0.0,0.0,14.0,16.0,12.0,0.0,0.0,0.0,1.0,10.0,16.0,16.0,12.0,0.0,0.0,0.0,3.0,12.0,14.0,16.0,9.0,0.0,0.0,0.0,0.0,0.0,5.0,16.0,15.0,0.0,0.0,0.0,0.0,0.0,4.0,16.0,14.0,0.0,0.0,0.0,0.0,0.0,1.0,13.0,16.0,1.0,0.0],-                [0.0,0.0,5.0,12.0,1.0,0.0,0.0,0.0,0.0,0.0,15.0,14.0,7.0,0.0,0.0,0.0,0.0,0.0,13.0,1.0,12.0,0.0,0.0,0.0,0.0,2.0,10.0,0.0,14.0,0.0,0.0,0.0,0.0,0.0,2.0,0.0,16.0,1.0,0.0,0.0,0.0,0.0,0.0,6.0,15.0,0.0,0.0,0.0,0.0,0.0,9.0,16.0,15.0,9.0,8.0,2.0,0.0,0.0,3.0,11.0,8.0,13.0,12.0,4.0],-                [0.0,2.0,9.0,15.0,14.0,9.0,3.0,0.0,0.0,4.0,13.0,8.0,9.0,16.0,8.0,0.0,0.0,0.0,0.0,6.0,14.0,15.0,3.0,0.0,0.0,0.0,0.0,11.0,14.0,2.0,0.0,0.0,0.0,0.0,0.0,2.0,15.0,11.0,0.0,0.0,0.0,0.0,0.0,0.0,2.0,15.0,4.0,0.0,0.0,1.0,5.0,6.0,13.0,16.0,6.0,0.0,0.0,2.0,12.0,12.0,13.0,11.0,0.0,0.0],-                [0.0,0.0,0.0,8.0,15.0,1.0,0.0,0.0,0.0,0.0,1.0,14.0,13.0,1.0,1.0,0.0,0.0,0.0,10.0,15.0,3.0,15.0,11.0,0.0,0.0,7.0,16.0,7.0,1.0,16.0,8.0,0.0,0.0,9.0,16.0,13.0,14.0,16.0,5.0,0.0,0.0,1.0,10.0,15.0,16.0,14.0,0.0,0.0,0.0,0.0,0.0,1.0,16.0,10.0,0.0,0.0,0.0,0.0,0.0,10.0,15.0,4.0,0.0,0.0],-                [0.0,5.0,12.0,13.0,16.0,16.0,2.0,0.0,0.0,11.0,16.0,15.0,8.0,4.0,0.0,0.0,0.0,8.0,14.0,11.0,1.0,0.0,0.0,0.0,0.0,8.0,16.0,16.0,14.0,0.0,0.0,0.0,0.0,1.0,6.0,6.0,16.0,0.0,0.0,0.0,0.0,0.0,0.0,5.0,16.0,3.0,0.0,0.0,0.0,1.0,5.0,15.0,13.0,0.0,0.0,0.0,0.0,4.0,15.0,16.0,2.0,0.0,0.0,0.0],-                [0.0,0.0,0.0,8.0,15.0,1.0,0.0,0.0,0.0,0.0,0.0,12.0,14.0,0.0,0.0,0.0,0.0,0.0,3.0,16.0,7.0,0.0,0.0,0.0,0.0,0.0,6.0,16.0,2.0,0.0,0.0,0.0,0.0,0.0,7.0,16.0,16.0,13.0,5.0,0.0,0.0,0.0,15.0,16.0,9.0,9.0,14.0,0.0,0.0,0.0,3.0,14.0,9.0,2.0,16.0,2.0,0.0,0.0,0.0,7.0,15.0,16.0,11.0,0.0],-                [0.0,0.0,1.0,8.0,15.0,10.0,0.0,0.0,0.0,3.0,13.0,15.0,14.0,14.0,0.0,0.0,0.0,5.0,10.0,0.0,10.0,12.0,0.0,0.0,0.0,0.0,3.0,5.0,15.0,10.0,2.0,0.0,0.0,0.0,16.0,16.0,16.0,16.0,12.0,0.0,0.0,1.0,8.0,12.0,14.0,8.0,3.0,0.0,0.0,0.0,0.0,10.0,13.0,0.0,0.0,0.0,0.0,0.0,0.0,11.0,9.0,0.0,0.0,0.0],-                [0.0,0.0,10.0,7.0,13.0,9.0,0.0,0.0,0.0,0.0,9.0,10.0,12.0,15.0,2.0,0.0,0.0,0.0,4.0,11.0,10.0,11.0,0.0,0.0,0.0,0.0,1.0,16.0,10.0,1.0,0.0,0.0,0.0,0.0,12.0,13.0,4.0,0.0,0.0,0.0,0.0,0.0,12.0,1.0,12.0,0.0,0.0,0.0,0.0,1.0,10.0,2.0,14.0,0.0,0.0,0.0,0.0,0.0,11.0,14.0,5.0,0.0,0.0,0.0],-                [0.0,0.0,6.0,14.0,4.0,0.0,0.0,0.0,0.0,0.0,11.0,16.0,10.0,0.0,0.0,0.0,0.0,0.0,8.0,14.0,16.0,2.0,0.0,0.0,0.0,0.0,1.0,12.0,12.0,11.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,11.0,3.0,0.0,0.0,0.0,0.0,0.0,0.0,5.0,11.0,0.0,0.0,0.0,1.0,4.0,4.0,7.0,16.0,2.0,0.0,0.0,7.0,16.0,16.0,13.0,11.0,1.0]-            ]--testNeighbourProbs :: [[Probability]]-testNeighbourProbs = neighbourProbabilities def testInput
+ test/Data/Algorithm/TSNE/PreparationSpec.hs view
@@ -0,0 +1,39 @@+module Data.Algorithm.TSNE.PreparationSpec (main, spec) where++import Data.Default (def)++import Test.Hspec+import Data.Algorithm.TSNE.Preparation+import Data.Algorithm.TSNE.Checks+import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Utils+import Data.Algorithm.TSNE.TestInput+import Data.Algorithm.TSNE.TestMisc++-- `main` is here so that this module can be run from GHCi on its own.  It is+-- not needed for automatic spec discovery.+main :: IO ()+main = hspec spec++u = undefined++spec :: Spec+spec = do+    let n = inputSize testInput+        w = inputValueSize testInput++    describe "testInput" $ do+        it "is right shape" $ do+            testInput `shouldSatisfy` has2DShape (64, 20)+        it "is valid" $ do+            inputIsValid testInput `shouldBe` Right ()+        it "has right size" $ do+            inputSize testInput `shouldBe` 20+        it "has right value size" $ do+            inputValueSize testInput `shouldBe` 64++    describe "neighbourProbabilities" $ do+        it "is right shape" $ do+            testNeighbourProbs `shouldSatisfy` has2DShape (n,n)++
+ test/Data/Algorithm/TSNE/Run2DSpec.hs view
@@ -0,0 +1,32 @@+module Data.Algorithm.TSNE.Run2DSpec (main, spec) where++import Data.Default (def)++import Test.Hspec+import Data.Algorithm.TSNE.Run2D+import Data.Algorithm.TSNE.Stepping+import Data.Algorithm.TSNE.Checks+import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Utils+import Data.Algorithm.TSNE.TestInput+import Data.Algorithm.TSNE.TestMisc++-- `main` is here so that this module can be run from GHCi on its own.  It is+-- not needed for automatic spec discovery.+main :: IO ()+main = hspec spec++u = undefined++spec :: Spec+spec = do+    let n = inputSize testInput+        w = inputValueSize testInput++    describe "initSolution2D" $ do+        it "is right shape" $+            initSolution2D n >>= (`shouldSatisfy` has2DShape (n,2))++    describe "initState" $ do+        it "is valid state" $+            initState2D n >>= (`shouldSatisfy` isRight . (isValidStateForInput 2 testInput))
+ test/Data/Algorithm/TSNE/Run3DSpec.hs view
@@ -0,0 +1,47 @@+module Data.Algorithm.TSNE.Run3DSpec (main, spec) where++import Data.Default (def)++import Test.Hspec+import Data.Algorithm.TSNE.Run3D+import Data.Algorithm.TSNE.Stepping+import Data.Algorithm.TSNE.Checks+import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Utils+import Data.Algorithm.TSNE.TestInput+import Data.Algorithm.TSNE.TestMisc++-- `main` is here so that this module can be run from GHCi on its own.  It is+-- not needed for automatic spec discovery.+main :: IO ()+main = hspec spec++u = undefined++spec :: Spec+spec = do+    let n = inputSize testInput+        w = inputValueSize testInput++    describe "initSolution3D" $ do+        it "is right shape" $+            initSolution3D n >>= (`shouldSatisfy` has2DShape (n,3))++    describe "initState" $ do+        it "is valid state" $+            initState3D n >>= (`shouldSatisfy` isRight . (isValidStateForInput 3 testInput))++    describe "qdist" $ do+        it "is right shape" $ do+            s <- initSolution3D n+            qdist s `shouldSatisfy` has2DShape (n,n)        ++    describe "qdist'" $ do+        it "is right shape" $ do+            s <- initSolution3D n+            qdist' s `shouldSatisfy` has2DShape (n,n)        ++    describe "gradients" $ do+        it "is right shape" $ do+            s <- initState3D n+            gradients testNeighbourProbs s `shouldSatisfy` has2DShape (n,3)
+ test/Data/Algorithm/TSNE/SteppingSpec.hs view
@@ -0,0 +1,29 @@+module Data.Algorithm.TSNE.SteppingSpec (main, spec) where++import Data.Default (def)++import Test.Hspec+import Data.Algorithm.TSNE.Stepping+import Data.Algorithm.TSNE.Run3D+import Data.Algorithm.TSNE.Checks+import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Utils+import Data.Algorithm.TSNE.TestInput+import Data.Algorithm.TSNE.TestMisc++-- `main` is here so that this module can be run from GHCi on its own.  It is+-- not needed for automatic spec discovery.+main :: IO ()+main = hspec spec++u = undefined++spec :: Spec+spec = do+    let n = inputSize testInput+        w = inputValueSize testInput++    describe "stepTSNE" $ do+        it "works" $ do +            s <- initState3D n+            stepTSNE def testInput testNeighbourProbs s `shouldSatisfy` isRight . (isValidStateForInput 3 testInput)
+ test/Data/Algorithm/TSNE/TestInput.hs view
@@ -0,0 +1,35 @@+module Data.Algorithm.TSNE.TestInput where++import Data.Default (def)++import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Preparation++-- first 20 digits from the Python sklearn digits dataset+testInput :: TSNEInput+testInput = [+                [0.0,0.0,5.0,13.0,9.0,1.0,0.0,0.0,0.0,0.0,13.0,15.0,10.0,15.0,5.0,0.0,0.0,3.0,15.0,2.0,0.0,11.0,8.0,0.0,0.0,4.0,12.0,0.0,0.0,8.0,8.0,0.0,0.0,5.0,8.0,0.0,0.0,9.0,8.0,0.0,0.0,4.0,11.0,0.0,1.0,12.0,7.0,0.0,0.0,2.0,14.0,5.0,10.0,12.0,0.0,0.0,0.0,0.0,6.0,13.0,10.0,0.0,0.0,0.0],+                [0.0,0.0,0.0,12.0,13.0,5.0,0.0,0.0,0.0,0.0,0.0,11.0,16.0,9.0,0.0,0.0,0.0,0.0,3.0,15.0,16.0,6.0,0.0,0.0,0.0,7.0,15.0,16.0,16.0,2.0,0.0,0.0,0.0,0.0,1.0,16.0,16.0,3.0,0.0,0.0,0.0,0.0,1.0,16.0,16.0,6.0,0.0,0.0,0.0,0.0,1.0,16.0,16.0,6.0,0.0,0.0,0.0,0.0,0.0,11.0,16.0,10.0,0.0,0.0],+                [0.0,0.0,0.0,4.0,15.0,12.0,0.0,0.0,0.0,0.0,3.0,16.0,15.0,14.0,0.0,0.0,0.0,0.0,8.0,13.0,8.0,16.0,0.0,0.0,0.0,0.0,1.0,6.0,15.0,11.0,0.0,0.0,0.0,1.0,8.0,13.0,15.0,1.0,0.0,0.0,0.0,9.0,16.0,16.0,5.0,0.0,0.0,0.0,0.0,3.0,13.0,16.0,16.0,11.0,5.0,0.0,0.0,0.0,0.0,3.0,11.0,16.0,9.0,0.0],+                [0.0,0.0,7.0,15.0,13.0,1.0,0.0,0.0,0.0,8.0,13.0,6.0,15.0,4.0,0.0,0.0,0.0,2.0,1.0,13.0,13.0,0.0,0.0,0.0,0.0,0.0,2.0,15.0,11.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,12.0,12.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,10.0,8.0,0.0,0.0,0.0,8.0,4.0,5.0,14.0,9.0,0.0,0.0,0.0,7.0,13.0,13.0,9.0,0.0,0.0],+                [0.0,0.0,0.0,1.0,11.0,0.0,0.0,0.0,0.0,0.0,0.0,7.0,8.0,0.0,0.0,0.0,0.0,0.0,1.0,13.0,6.0,2.0,2.0,0.0,0.0,0.0,7.0,15.0,0.0,9.0,8.0,0.0,0.0,5.0,16.0,10.0,0.0,16.0,6.0,0.0,0.0,4.0,15.0,16.0,13.0,16.0,1.0,0.0,0.0,0.0,0.0,3.0,15.0,10.0,0.0,0.0,0.0,0.0,0.0,2.0,16.0,4.0,0.0,0.0],+                [0.0,0.0,12.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,14.0,16.0,16.0,14.0,0.0,0.0,0.0,0.0,13.0,16.0,15.0,10.0,1.0,0.0,0.0,0.0,11.0,16.0,16.0,7.0,0.0,0.0,0.0,0.0,0.0,4.0,7.0,16.0,7.0,0.0,0.0,0.0,0.0,0.0,4.0,16.0,9.0,0.0,0.0,0.0,5.0,4.0,12.0,16.0,4.0,0.0,0.0,0.0,9.0,16.0,16.0,10.0,0.0,0.0],+                [0.0,0.0,0.0,12.0,13.0,0.0,0.0,0.0,0.0,0.0,5.0,16.0,8.0,0.0,0.0,0.0,0.0,0.0,13.0,16.0,3.0,0.0,0.0,0.0,0.0,0.0,14.0,13.0,0.0,0.0,0.0,0.0,0.0,0.0,15.0,12.0,7.0,2.0,0.0,0.0,0.0,0.0,13.0,16.0,13.0,16.0,3.0,0.0,0.0,0.0,7.0,16.0,11.0,15.0,8.0,0.0,0.0,0.0,1.0,9.0,15.0,11.0,3.0,0.0],+                [0.0,0.0,7.0,8.0,13.0,16.0,15.0,1.0,0.0,0.0,7.0,7.0,4.0,11.0,12.0,0.0,0.0,0.0,0.0,0.0,8.0,13.0,1.0,0.0,0.0,4.0,8.0,8.0,15.0,15.0,6.0,0.0,0.0,2.0,11.0,15.0,15.0,4.0,0.0,0.0,0.0,0.0,0.0,16.0,5.0,0.0,0.0,0.0,0.0,0.0,9.0,15.0,1.0,0.0,0.0,0.0,0.0,0.0,13.0,5.0,0.0,0.0,0.0,0.0],+                [0.0,0.0,9.0,14.0,8.0,1.0,0.0,0.0,0.0,0.0,12.0,14.0,14.0,12.0,0.0,0.0,0.0,0.0,9.0,10.0,0.0,15.0,4.0,0.0,0.0,0.0,3.0,16.0,12.0,14.0,2.0,0.0,0.0,0.0,4.0,16.0,16.0,2.0,0.0,0.0,0.0,3.0,16.0,8.0,10.0,13.0,2.0,0.0,0.0,1.0,15.0,1.0,3.0,16.0,8.0,0.0,0.0,0.0,11.0,16.0,15.0,11.0,1.0,0.0],+                [0.0,0.0,11.0,12.0,0.0,0.0,0.0,0.0,0.0,2.0,16.0,16.0,16.0,13.0,0.0,0.0,0.0,3.0,16.0,12.0,10.0,14.0,0.0,0.0,0.0,1.0,16.0,1.0,12.0,15.0,0.0,0.0,0.0,0.0,13.0,16.0,9.0,15.0,2.0,0.0,0.0,0.0,0.0,3.0,0.0,9.0,11.0,0.0,0.0,0.0,0.0,0.0,9.0,15.0,4.0,0.0,0.0,0.0,9.0,12.0,13.0,3.0,0.0,0.0],+                [0.0,0.0,1.0,9.0,15.0,11.0,0.0,0.0,0.0,0.0,11.0,16.0,8.0,14.0,6.0,0.0,0.0,2.0,16.0,10.0,0.0,9.0,9.0,0.0,0.0,1.0,16.0,4.0,0.0,8.0,8.0,0.0,0.0,4.0,16.0,4.0,0.0,8.0,8.0,0.0,0.0,1.0,16.0,5.0,1.0,11.0,3.0,0.0,0.0,0.0,12.0,12.0,10.0,10.0,0.0,0.0,0.0,0.0,1.0,10.0,13.0,3.0,0.0,0.0],+                [0.0,0.0,0.0,0.0,14.0,13.0,1.0,0.0,0.0,0.0,0.0,5.0,16.0,16.0,2.0,0.0,0.0,0.0,0.0,14.0,16.0,12.0,0.0,0.0,0.0,1.0,10.0,16.0,16.0,12.0,0.0,0.0,0.0,3.0,12.0,14.0,16.0,9.0,0.0,0.0,0.0,0.0,0.0,5.0,16.0,15.0,0.0,0.0,0.0,0.0,0.0,4.0,16.0,14.0,0.0,0.0,0.0,0.0,0.0,1.0,13.0,16.0,1.0,0.0],+                [0.0,0.0,5.0,12.0,1.0,0.0,0.0,0.0,0.0,0.0,15.0,14.0,7.0,0.0,0.0,0.0,0.0,0.0,13.0,1.0,12.0,0.0,0.0,0.0,0.0,2.0,10.0,0.0,14.0,0.0,0.0,0.0,0.0,0.0,2.0,0.0,16.0,1.0,0.0,0.0,0.0,0.0,0.0,6.0,15.0,0.0,0.0,0.0,0.0,0.0,9.0,16.0,15.0,9.0,8.0,2.0,0.0,0.0,3.0,11.0,8.0,13.0,12.0,4.0],+                [0.0,2.0,9.0,15.0,14.0,9.0,3.0,0.0,0.0,4.0,13.0,8.0,9.0,16.0,8.0,0.0,0.0,0.0,0.0,6.0,14.0,15.0,3.0,0.0,0.0,0.0,0.0,11.0,14.0,2.0,0.0,0.0,0.0,0.0,0.0,2.0,15.0,11.0,0.0,0.0,0.0,0.0,0.0,0.0,2.0,15.0,4.0,0.0,0.0,1.0,5.0,6.0,13.0,16.0,6.0,0.0,0.0,2.0,12.0,12.0,13.0,11.0,0.0,0.0],+                [0.0,0.0,0.0,8.0,15.0,1.0,0.0,0.0,0.0,0.0,1.0,14.0,13.0,1.0,1.0,0.0,0.0,0.0,10.0,15.0,3.0,15.0,11.0,0.0,0.0,7.0,16.0,7.0,1.0,16.0,8.0,0.0,0.0,9.0,16.0,13.0,14.0,16.0,5.0,0.0,0.0,1.0,10.0,15.0,16.0,14.0,0.0,0.0,0.0,0.0,0.0,1.0,16.0,10.0,0.0,0.0,0.0,0.0,0.0,10.0,15.0,4.0,0.0,0.0],+                [0.0,5.0,12.0,13.0,16.0,16.0,2.0,0.0,0.0,11.0,16.0,15.0,8.0,4.0,0.0,0.0,0.0,8.0,14.0,11.0,1.0,0.0,0.0,0.0,0.0,8.0,16.0,16.0,14.0,0.0,0.0,0.0,0.0,1.0,6.0,6.0,16.0,0.0,0.0,0.0,0.0,0.0,0.0,5.0,16.0,3.0,0.0,0.0,0.0,1.0,5.0,15.0,13.0,0.0,0.0,0.0,0.0,4.0,15.0,16.0,2.0,0.0,0.0,0.0],+                [0.0,0.0,0.0,8.0,15.0,1.0,0.0,0.0,0.0,0.0,0.0,12.0,14.0,0.0,0.0,0.0,0.0,0.0,3.0,16.0,7.0,0.0,0.0,0.0,0.0,0.0,6.0,16.0,2.0,0.0,0.0,0.0,0.0,0.0,7.0,16.0,16.0,13.0,5.0,0.0,0.0,0.0,15.0,16.0,9.0,9.0,14.0,0.0,0.0,0.0,3.0,14.0,9.0,2.0,16.0,2.0,0.0,0.0,0.0,7.0,15.0,16.0,11.0,0.0],+                [0.0,0.0,1.0,8.0,15.0,10.0,0.0,0.0,0.0,3.0,13.0,15.0,14.0,14.0,0.0,0.0,0.0,5.0,10.0,0.0,10.0,12.0,0.0,0.0,0.0,0.0,3.0,5.0,15.0,10.0,2.0,0.0,0.0,0.0,16.0,16.0,16.0,16.0,12.0,0.0,0.0,1.0,8.0,12.0,14.0,8.0,3.0,0.0,0.0,0.0,0.0,10.0,13.0,0.0,0.0,0.0,0.0,0.0,0.0,11.0,9.0,0.0,0.0,0.0],+                [0.0,0.0,10.0,7.0,13.0,9.0,0.0,0.0,0.0,0.0,9.0,10.0,12.0,15.0,2.0,0.0,0.0,0.0,4.0,11.0,10.0,11.0,0.0,0.0,0.0,0.0,1.0,16.0,10.0,1.0,0.0,0.0,0.0,0.0,12.0,13.0,4.0,0.0,0.0,0.0,0.0,0.0,12.0,1.0,12.0,0.0,0.0,0.0,0.0,1.0,10.0,2.0,14.0,0.0,0.0,0.0,0.0,0.0,11.0,14.0,5.0,0.0,0.0,0.0],+                [0.0,0.0,6.0,14.0,4.0,0.0,0.0,0.0,0.0,0.0,11.0,16.0,10.0,0.0,0.0,0.0,0.0,0.0,8.0,14.0,16.0,2.0,0.0,0.0,0.0,0.0,1.0,12.0,12.0,11.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,11.0,3.0,0.0,0.0,0.0,0.0,0.0,0.0,5.0,11.0,0.0,0.0,0.0,1.0,4.0,4.0,7.0,16.0,2.0,0.0,0.0,7.0,16.0,16.0,13.0,11.0,1.0]+            ]++testNeighbourProbs :: [[Probability]]+testNeighbourProbs = neighbourProbabilities def testInput+
+ test/Data/Algorithm/TSNE/TestMisc.hs view
@@ -0,0 +1,5 @@+module Data.Algorithm.TSNE.TestMisc where++isRight :: Either a b -> Bool+isRight (Left _) = False+isRight (Right _) = True
tsne.cabal view
@@ -1,5 +1,5 @@ name:                tsne-version:             1.2.0+version:             1.3.0 synopsis:            t-SNE description:         Pure Haskell implementation of the t-SNE dimension reduction algorithm. homepage:            https://bitbucket.org/robagar/haskell-tsne@@ -16,9 +16,12 @@   hs-source-dirs:      src   exposed-modules:     Data.Algorithm.TSNE,                        Data.Algorithm.TSNE.Types, -                       Data.Algorithm.TSNE.Internals,                         Data.Algorithm.TSNE.Utils,-                       Data.Algorithm.TSNE.Checks +                       Data.Algorithm.TSNE.Checks, +                       Data.Algorithm.TSNE.Preparation, +                       Data.Algorithm.TSNE.Stepping, +                       Data.Algorithm.TSNE.Run2D, +                       Data.Algorithm.TSNE.Run3D    build-depends:       base >= 4.7 && < 5,                        data-default,                        deepseq,@@ -31,9 +34,14 @@   hs-source-dirs:      test   main-is:             Spec.hs   other-modules:       Data.Algorithm.TSNESpec,-                       Data.Algorithm.TSNE.InternalsSpec,+                       Data.Algorithm.TSNE.PreparationSpec,+                       Data.Algorithm.TSNE.SteppingSpec,                        Data.Algorithm.TSNE.UtilsSpec,-                       Data.Algorithm.TSNE.ChecksSpec+                       Data.Algorithm.TSNE.ChecksSpec,+                       Data.Algorithm.TSNE.Run3DSpec,+                       Data.Algorithm.TSNE.Run2DSpec,+                       Data.Algorithm.TSNE.TestInput,+                       Data.Algorithm.TSNE.TestMisc   build-depends:       base,                        hspec,                        data-default,