packages feed

tsne (empty) → 1.0.0.1

raw patch · 14 files changed

+793/−0 lines, 14 filesdep +basedep +data-defaultdep +deepseqsetup-changed

Dependencies added: base, data-default, deepseq, hspec, normaldistribution, pipes, tsne

Files

+ LICENSE view
@@ -0,0 +1,165 @@+                   GNU LESSER GENERAL PUBLIC LICENSE+                       Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.+++  This version of the GNU Lesser General Public License incorporates+the terms and conditions of version 3 of the GNU General Public+License, supplemented by the additional permissions listed below.++  0. Additional Definitions.++  As used herein, "this License" refers to version 3 of the GNU Lesser+General Public License, and the "GNU GPL" refers to version 3 of the GNU+General Public License.++  "The Library" refers to a covered work governed by this License,+other than an Application or a Combined Work as defined below.++  An "Application" is any work that makes use of an interface provided+by the Library, but which is not otherwise based on the Library.+Defining a subclass of a class defined by the Library is deemed a mode+of using an interface provided by the Library.++  A "Combined Work" is a work produced by combining or linking an+Application with the Library.  The particular version of the Library+with which the Combined Work was made is also called the "Linked+Version".++  The "Minimal Corresponding Source" for a Combined Work means the+Corresponding Source for the Combined Work, excluding any source code+for portions of the Combined Work that, considered in isolation, are+based on the Application, and not on the Linked Version.++  The "Corresponding Application Code" for a Combined Work means the+object code and/or source code for the Application, including any data+and utility programs needed for reproducing the Combined Work from the+Application, but excluding the System Libraries of the Combined Work.++  1. Exception to Section 3 of the GNU GPL.++  You may convey a covered work under sections 3 and 4 of this License+without being bound by section 3 of the GNU GPL.++  2. Conveying Modified Versions.++  If you modify a copy of the Library, and, in your modifications, a+facility refers to a function or data to be supplied by an Application+that uses the facility (other than as an argument passed when the+facility is invoked), then you may convey a copy of the modified+version:++   a) under this License, provided that you make a good faith effort to+   ensure that, in the event an Application does not supply the+   function or data, the facility still operates, and performs+   whatever part of its purpose remains meaningful, or++   b) under the GNU GPL, with none of the additional permissions of+   this License applicable to that copy.++  3. Object Code Incorporating Material from Library Header Files.++  The object code form of an Application may incorporate material from+a header file that is part of the Library.  You may convey such object+code under terms of your choice, provided that, if the incorporated+material is not limited to numerical parameters, data structure+layouts and accessors, or small macros, inline functions and templates+(ten or fewer lines in length), you do both of the following:++   a) Give prominent notice with each copy of the object code that the+   Library is used in it and that the Library and its use are+   covered by this License.++   b) Accompany the object code with a copy of the GNU GPL and this license+   document.++  4. Combined Works.++  You may convey a Combined Work under terms of your choice that,+taken together, effectively do not restrict modification of the+portions of the Library contained in the Combined Work and reverse+engineering for debugging such modifications, if you also do each of+the following:++   a) Give prominent notice with each copy of the Combined Work that+   the Library is used in it and that the Library and its use are+   covered by this License.++   b) Accompany the Combined Work with a copy of the GNU GPL and this license+   document.++   c) For a Combined Work that displays copyright notices during+   execution, include the copyright notice for the Library among+   these notices, as well as a reference directing the user to the+   copies of the GNU GPL and this license document.++   d) Do one of the following:++       0) Convey the Minimal Corresponding Source under the terms of this+       License, and the Corresponding Application Code in a form+       suitable for, and under terms that permit, the user to+       recombine or relink the Application with a modified version of+       the Linked Version to produce a modified Combined Work, in the+       manner specified by section 6 of the GNU GPL for conveying+       Corresponding Source.++       1) Use a suitable shared library mechanism for linking with the+       Library.  A suitable mechanism is one that (a) uses at run time+       a copy of the Library already present on the user's computer+       system, and (b) will operate properly with a modified version+       of the Library that is interface-compatible with the Linked+       Version.++   e) Provide Installation Information, but only if you would otherwise+   be required to provide such information under section 6 of the+   GNU GPL, and only to the extent that such information is+   necessary to install and execute a modified version of the+   Combined Work produced by recombining or relinking the+   Application with a modified version of the Linked Version. (If+   you use option 4d0, the Installation Information must accompany+   the Minimal Corresponding Source and Corresponding Application+   Code. If you use option 4d1, you must provide the Installation+   Information in the manner specified by section 6 of the GNU GPL+   for conveying Corresponding Source.)++  5. Combined Libraries.++  You may place library facilities that are a work based on the+Library side by side in a single library together with other library+facilities that are not Applications and are not covered by this+License, and convey such a combined library under terms of your+choice, if you do both of the following:++   a) Accompany the combined library with a copy of the same work based+   on the Library, uncombined with any other library facilities,+   conveyed under the terms of this License.++   b) Give prominent notice with the combined library that part of it+   is a work based on the Library, and explaining where to find the+   accompanying uncombined form of the same work.++  6. Revised Versions of the GNU Lesser General Public License.++  The Free Software Foundation may publish revised and/or new versions+of the GNU Lesser General Public License from time to time. Such new+versions will be similar in spirit to the present version, but may+differ in detail to address new problems or concerns.++  Each version is given a distinguishing version number. If the+Library as you received it specifies that a certain numbered version+of the GNU Lesser General Public License "or any later version"+applies to it, you have the option of following the terms and+conditions either of that published version or of any later version+published by the Free Software Foundation. If the Library as you+received it does not specify a version number of the GNU Lesser+General Public License, you may choose any version of the GNU Lesser+General Public License ever published by the Free Software Foundation.++  If the Library as you received it specifies that a proxy can decide+whether future versions of the GNU Lesser General Public License shall+apply, that proxy's public statement of acceptance of any version is+permanent authorization for you to choose that version for the+Library.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ example/Main.hs view
@@ -0,0 +1,45 @@+module Main where++import System.Environment(getArgs)+import System.Exit+import Control.Monad(when)+import Data.Default(def)+import Pipes+import Data.Algorithm.TSNE+++main :: IO ()+main = do+    putStrLn "Haskell tSNE example"++    args <- getArgs+    let argc = length args+    when (argc < 1 || argc > 2) $ do+        putStrLn "Usage haskell_tsne_example {input file name} [{num input values}]"+        exitFailure++    let inputFileName = head args+    inputData <- readDataFile inputFileName++    let n = length inputData+    putStrLn $ "total input: " ++ show n++    let n' = if (argc < 2) then n else (read (args !! 1)) ++    putStrLn $ "using: " ++ show n'++    forTsne3D outputResult def $ take n' inputData+    +    --runEffect $ for (tsne3D def $ take n' inputData) $ \r -> do+    --    lift $ outputResult r++readDataFile :: FilePath -> IO [[Double]]+readDataFile f = do+    d <- readFile f+    return $ map read (lines d)++outputResult :: TSNEOutput3D -> IO ()+outputResult s = do+    putStrLn $ "iteration: " ++ (show.tsneIteration) s+    putStrLn $ "cost: " ++ (show.tsneCost) s+
+ src/Data/Algorithm/TSNE.hs view
@@ -0,0 +1,26 @@+module Data.Algorithm.TSNE ( +        tsne3D,+        forTsne3D,+        TSNEOptions(..),+        TSNEOutput3D(..)+    ) where++import Pipes++import Data.Algorithm.TSNE.Types+import Data.Algorithm.TSNE.Internals+import Data.Algorithm.TSNE.Utils++-- | Generates an infinite stream of 3D tSNE iterations.+tsne3D :: TSNEOptions -> TSNEInput -> Producer TSNEOutput3D IO ()+tsne3D opts input = do+    st <- liftIO $ initState $ length input+    runTSNE opts input ps st+        where ps = neighbourProbabilities opts input++-- | Executes an IO action for each iteration of the 3D tSNE algorithm.+forTsne3D :: (TSNEOutput3D -> IO ()) -> TSNEOptions -> TSNEInput -> IO ()+forTsne3D action opts input = do+    runEffect $ for (tsne3D opts input) $ \o -> do+        lift $ action o    +
+ src/Data/Algorithm/TSNE/Checks.hs view
@@ -0,0 +1,19 @@+module Data.Algorithm.TSNE.Checks where++import Data.Algorithm.TSNE.Types++isSquare :: Int -> [[a]] -> Bool+isSquare 0 [] = True+isSquare _ [] = False+isSquare n xss = length xss == n && all (\xs -> length xs == n) xss++has2DShape :: (Int, Int) -> [[a]] -> Bool+has2DShape (w,h) xss = length xss == h && all (\xs -> length xs == w) xss++shape2D :: [[a]] -> (Int, Int)+shape2D [] = (undefined, 0)+shape2D xss = (length (head xss), length xss)++isRectangular :: [[a]] -> Bool+isRectangular xss = has2DShape (shape2D xss) xss+
+ src/Data/Algorithm/TSNE/Internals.hs view
@@ -0,0 +1,177 @@+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    ++initState :: Int -> IO TSNEState+initState 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]++runTSNE :: TSNEOptions -> TSNEInput -> [[Probability]] -> TSNEState -> Producer TSNEOutput3D IO ()+runTSNE opts vs ps st = do+    let st' = force $ stepTSNE opts vs ps st+    yield $ output3D ps st'+    runTSNE 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++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/Types.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}++module Data.Algorithm.TSNE.Types where++import GHC.Generics (Generic)+import Control.DeepSeq+import Data.Default+++data TSNEOptions = TSNEOptions {+    tsnePerplexity :: Int,+    tsneLearningRate :: Double+}++type TSNEInputValue =  [Double]+type TSNEInput =  [TSNEInputValue]++type Position3D = (Double,Double,Double)++data TSNEOutput3D = TSNEOutput3D {+    tsneIteration :: Int,+    tsneSolution3D :: [Position3D],+    tsneCost :: Double+} deriving (Show, Eq)++instance Default TSNEOptions where+    def = TSNEOptions 30 10++type Probability = Double+type Gain = Double+type Delta = Double+type Gradient = Double+type Entropy = Double++data TSNEState = TSNEState {+    stIteration :: Int,+    stSolution :: [[Double]],+    stGains :: [[Gain]],+    stDeltas :: [[Delta]]+} deriving (Show, Generic, NFData)
+ src/Data/Algorithm/TSNE/Utils.hs view
@@ -0,0 +1,58 @@+module Data.Algorithm.TSNE.Utils where++import Data.List(foldr, transpose)++infinity :: Double+infinity = read "Infinity"+ +distanceSquared :: [Double] -> [Double] -> Double+distanceSquared as bs = foldr d 0 (zip as bs)+    where d (a,b) t  = t + (a-b) * (a-b)++symmetrize :: [[Double]] -> [[Double]]+symmetrize m = (zipWith.zipWith) f m (transpose m)+    where +        f :: Double -> Double -> Double+        f x y = max a 1e-100+            where a = (x + y) / (2 * (realToFrac.length) m) ++recenter :: [[Double]] -> [[Double]]+recenter ss = map r ss+    where +        r s = subtract (mean s) <$> s+        mean s = sum s / (realToFrac.length) s+         +qdist :: [[Double]] -> [[Double]]+qdist ss = symmetricalMatrixFromTopRight $ qd (transpose ss)+    where+        qd [] = []+        qd ps = [qr ps] ++ qd (tail ps)+        qr :: [[Double]] -> [Double]+        qr ps =  [0::Double] ++ (q (head ps) <$> tail ps)+        q as bs = 1 / (1 + s)+            where+                s = sum $ zipWith f as bs+                f a b = (a-b) * (a-b) ++qdist' :: [[Double]] -> [[Double]]+qdist' ss = (map.map) f qd+    where+        qd = qdist ss+        f :: Double -> Double +        f q = max (q / sumsum qd) 1e-100+ +sumsum :: [[Double]] -> Double+sumsum m = sum $ sum <$> m ++reprep :: a -> [[a]]+reprep = repeat.repeat++symmetricalMatrixFromTopRight :: [[a]] -> [[a]]+symmetricalMatrixFromTopRight tr = zipWith (++) bl tr+    where+        bl = zipWith take [0..] (transpose m)+        m = zipWith (++) ebl tr+        ebl = zipWith take [0..] (reprep undefined)+++
+ test/Data/Algorithm/TSNE/ChecksSpec.hs view
@@ -0,0 +1,55 @@+module Data.Algorithm.TSNE.ChecksSpec (main, spec) where++import Test.Hspec+import Data.Algorithm.TSNE.Checks++-- `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+    describe "isSquare" $ do+        it "empty is 0x0 square" $ do+            isSquare 0 [] `shouldBe` True+        it "empty is not 1x1 square" $ do+            isSquare 1 [] `shouldBe` False+        it "single is 1x1 square" $ do+            isSquare 1 [[u]] `shouldBe` True+        it "single is not 0x0 square" $ do+            isSquare 0 [[u]] `shouldBe` False+        it "2x2 square" $ do+            isSquare 2 [[u,u],[u,u]] `shouldBe` True+        it "2x2 is not 0x0" $ do+            isSquare 0 [[u,u],[u,u]] `shouldBe` False+        it "2x2 is not 0x0" $ do+            isSquare 1 [[u,u],[u,u]] `shouldBe` False+        it "2x2 is not 3x3" $ do+            isSquare 3 [[u,u],[u,u]] `shouldBe` False+        it "2x1 is not 1x1" $ do+            isSquare 1 [[u,u]] `shouldBe` False+        it "2x1 is not 2x2" $ do+            isSquare 2 [[u,u]] `shouldBe` False+        it "1x2 is not 1x1" $ do+            isSquare 1 [[u,u]] `shouldBe` False+        it "1x2 is not 2x2" $ do+            isSquare 2 [[u,u]] `shouldBe` False++    describe "has2DShape" $ do+        it "empty is 0x0" $ do+            has2DShape (0,0) [] `shouldBe` True+        --it "empty is not 1x0" $ do+        --    has2DShape (1,0) [] `shouldBe` False+        it "empty is not 0x1" $ do+            has2DShape (0,1) [] `shouldBe` False+        it "1x2 is 1x2" $ do+            has2DShape (1,2) [[u],[u]] `shouldBe` True+        it "2x1 is 2x1" $ do+            has2DShape (2,1) [[u,u]] `shouldBe` True+        it "1x2 is not 2x1" $ do+            has2DShape (2,1) [[u],[u]] `shouldBe` False+        it "2x1 is not 1x2" $ do+            has2DShape (1,2) [[u,u]] `shouldBe` False
+ test/Data/Algorithm/TSNE/InternalsSpec.hs view
@@ -0,0 +1,97 @@+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/UtilsSpec.hs view
@@ -0,0 +1,33 @@+module Data.Algorithm.TSNE.UtilsSpec (main, spec) where++import Test.Hspec+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+    describe "infinity" $ do+        it "greater than 1e99" $ do+            infinity `shouldSatisfy` (> 1e99)++    describe "symmetricalMatrixFromTopRight" $ do+        it "passed nothing returns nothing" $ do+            symmetricalMatrixFromTopRight [] `shouldBe` ([] :: [[Double]])+        it "passed one value returns one value" $ do+            symmetricalMatrixFromTopRight [[42]] `shouldBe` ([[42]] :: [[Double]])+        it "passed 2x2 top right" $ do+            symmetricalMatrixFromTopRight [["a1", "a2"], ["b2"]] `shouldBe` [["a1", "a2"], ["a2", "b2"]] +        it "passed 3x3 top right" $ do+            symmetricalMatrixFromTopRight [["a1", "a2", "a3"], ["b2", "b3"], ["c3"]] `shouldBe` [["a1", "a2", "a3"], ["a2", "b2", "b3"], ["a3", "b3", "c3"]] ++    describe "qdist" $ do+        it "passed nothing return nothing" $ do+            pending+            --qdist [] `shouldBe` ([] :: [[Double]])+
+ test/Data/Algorithm/TSNESpec.hs view
@@ -0,0 +1,17 @@+module Data.Algorithm.TSNESpec (main, spec) where++import Test.Hspec+import Data.Default (def)+import Data.Algorithm.TSNE++-- `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++spec :: Spec+spec = do+  describe "tsne" $ do+    it "passed nothing returns nothing" $ do+        pending+      --tsne def [[]] `shouldReturn` []
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ tsne.cabal view
@@ -0,0 +1,58 @@+name:                tsne+version:             1.0.0.1+synopsis:            t-SNE+description:         Pure Haskell implementation of the t-SNE dimension reduction algorithm.+homepage:            https://bitbucket.org/robagar/haskell-tsne+license:             LGPL+license-file:        LICENSE+author:              Rob Agar+maintainer:          robagar@fastmail.net+copyright:           2017 Rob Agar+category:            Algorithms+build-type:          Simple+cabal-version:       >=1.10++library+  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 +  build-depends:       base >= 4.7 && < 5,+                       data-default,+                       deepseq,+                       normaldistribution,+                       pipes+  default-language:    Haskell2010++test-suite tSNE-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  other-modules:       Data.Algorithm.TSNESpec,+                       Data.Algorithm.TSNE.InternalsSpec,+                       Data.Algorithm.TSNE.UtilsSpec,+                       Data.Algorithm.TSNE.ChecksSpec+  build-depends:       base,+                       hspec,+                       data-default,+                       tsne+  ghc-options:         -threaded +                       -rtsopts +                       -with-rtsopts=-N+  default-language:    Haskell2010++executable haskell_tsne_example+  hs-source-dirs:      example+  main-is:             Main.hs+  build-depends:       base,+                       data-default,+                       pipes,+                       tsne+  ghc-options:         -threaded+  default-language:    Haskell2010++source-repository head+  type:     mercurial+  location: https://bitbucket.org/robagar/haskell-tsne