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