packages feed

too-many-cells-2.1.0.1: src/TooManyCells/MakeTree/Adjacency.hs

{- TooManyCells.MakeTree.Adjacency
Gregory W. Schwartz

Collects functions pertaining to calculating similarities and adjacency
matrices.
-}

module TooManyCells.MakeTree.Adjacency
    ( cosineSimilarityDense
    , cosineSimilaritySparse
    , getDenseAdjacencyMat
    , getSparseAdjacencyMat
    ) where

-- Remote
import Data.List (foldl')
import qualified Numeric.LinearAlgebra as H
import qualified Data.Sparse.Common as S

-- Local
import TooManyCells.MakeTree.Types
import TooManyCells.Matrix.Types
import TooManyCells.Matrix.Utility

-- | Get the cosine similarity between two vectors.
cosineSimilarityDense :: H.Vector Double -> H.Vector Double -> Double
cosineSimilarityDense v w = H.dot v w / (H.norm_2 v * H.norm_2 w)

-- | Get the cosine similarity between two vectors.
cosineSimilaritySparse :: S.SpVector Double -> S.SpVector Double -> Double
cosineSimilaritySparse v w = S.dot v w / (norm2 v * norm2 w)
  where
    norm2 = sqrt . foldl' (+) 0 . fmap (** 2)

-- | Get an adjacency matrix based on a matrix where each row is an observation
-- and the adjacencies are cosine similarities.
getDenseAdjacencyMat :: MatObsRow -> AdjacencyMat
getDenseAdjacencyMat (MatObsRow sparseMat) =
    AdjacencyMat $ H.assoc (size, size) 0 assocList
  where
    mat = sparseToHMat sparseMat
    size :: Int
    size       = H.rows mat
    getSim :: Int -> Int -> Double
    getSim i j = if i == j
                    then 0
                    else cosineSimilarityDense (mat H.! i) (mat H.! j)
    assocList :: [((Int, Int), Double)]
    assocList  = (\i j -> ((i, j), getSim i j))
             <$> [0,1 .. (size - 1)]
             <*> [0,1 .. (size - 1)]

-- | Get an adjacency matrix based on a matrix where each row is an observation
-- and the adjacencies are cosine similarities.
getSparseAdjacencyMat :: MatObsRow -> AdjacencyMat
getSparseAdjacencyMat (MatObsRow mat) =
    AdjacencyMat $ H.assoc (size, size) 0 assocList
  where
    size :: Int
    size       = S.nrows mat
    getSim :: Int -> Int -> Double
    getSim i j = if i == j
                    then 0
                    else cosineSimilaritySparse (S.extractRow mat i) (S.extractRow mat j)
    assocList :: [((Int, Int), Double)]
    assocList  = (\i j -> ((i, j), getSim i j))
             <$> [0,1 .. (size - 1)]
             <*> [0,1 .. (size - 1)]