{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Datamining.Clustering.DSOMInternal where
import Control.DeepSeq (NFData)
import Data.Datamining.Clustering.Classifier (Classifier (..))
import qualified Data.Foldable as F (Foldable, foldr)
import Data.List (foldl', minimumBy)
import Data.Ord (comparing)
import GHC.Generics (Generic)
import qualified Math.Geometry.Grid as G (FiniteGrid (..),
Grid (..))
import qualified Math.Geometry.GridMap as GM (GridMap (..))
import Prelude hiding (lookup)
data DSOM gm x k p = DSOM
{
gridMap :: gm p,
learningRate :: (x -> x -> x -> x),
difference :: p -> p -> x,
makeSimilar :: p -> x -> p -> p
} deriving (Generic, NFData)
instance (F.Foldable gm) => F.Foldable (DSOM gm x k) where
foldr f x g = F.foldr f x (gridMap g)
instance (G.Grid (gm p)) => G.Grid (DSOM gm x k p) where
type Index (DSOM gm x k p) = G.Index (gm p)
type Direction (DSOM gm x k p) = G.Direction (gm p)
indices = G.indices . gridMap
distance = G.distance . gridMap
neighbours = G.neighbours . gridMap
contains = G.contains . gridMap
viewpoint = G.viewpoint . gridMap
directionTo = G.directionTo . gridMap
tileCount = G.tileCount . gridMap
null = G.null . gridMap
nonNull = G.nonNull . gridMap
instance
(F.Foldable gm, GM.GridMap gm p, G.FiniteGrid (GM.BaseGrid gm p)) =>
GM.GridMap (DSOM gm x k) p where
type BaseGrid (DSOM gm x k) p = GM.BaseGrid gm p
toGrid = GM.toGrid . gridMap
toMap = GM.toMap . gridMap
mapWithKey = error "Not implemented"
delete k = withGridMap (GM.delete k)
adjustWithKey f k = withGridMap (GM.adjustWithKey f k)
insertWithKey f k v = withGridMap (GM.insertWithKey f k v)
alter f k = withGridMap (GM.alter f k)
filterWithKey f = withGridMap (GM.filterWithKey f)
withGridMap :: (gm p -> gm p) -> DSOM gm x k p -> DSOM gm x k p
withGridMap f s = s { gridMap=gm' }
where gm = gridMap s
gm' = f gm
toGridMap :: GM.GridMap gm p => DSOM gm x k p -> gm p
toGridMap = gridMap
adjustNode
:: (G.FiniteGrid (gm p), GM.GridMap gm p,
k ~ G.Index (gm p), k ~ G.Index (GM.BaseGrid gm p),
Ord k, Num x, Fractional x) =>
gm p -> (p -> x -> p -> p) -> (p -> p -> x) -> (x -> x -> x) -> p -> k -> k
-> (p -> p)
adjustNode gm fms fd fr target bmu k = fms target amount
where diff = fd (gm GM.! k) target
dist = scaleDistance (G.distance gm bmu k)
(G.maxPossibleDistance gm)
amount = fr diff dist
scaleDistance :: (Num a, Fractional a) => Int -> Int -> a
scaleDistance d dMax
| dMax == 0 = 0
| otherwise = fromIntegral d / fromIntegral dMax
trainNeighbourhood
:: (G.FiniteGrid (gm p), GM.GridMap gm p,
k ~ G.Index (gm p), k ~ G.Index (GM.BaseGrid gm p),
Ord k, Num x, Fractional x) =>
DSOM gm x t p -> k -> p -> DSOM gm x k p
trainNeighbourhood s bmu target = s { gridMap=gm' }
where gm = gridMap s
gm' = GM.mapWithKey (adjustNode gm fms fd fr target bmu) gm
fms = makeSimilar s
fd = difference s
fr = (learningRate s) bmuDiff
bmuDiff = (difference s) (gm GM.! bmu) target
justTrain
:: (G.FiniteGrid (gm p), GM.GridMap gm p, GM.GridMap gm x,
k ~ G.Index (gm p), k ~ G.Index (gm x),
k ~ G.Index (GM.BaseGrid gm p), k ~ G.Index (GM.BaseGrid gm x),
Ord k, Ord x, Num x, Fractional x) =>
DSOM gm x t p -> p -> DSOM gm x k p
justTrain s p = trainNeighbourhood s bmu p
where ds = GM.toList . GM.map (difference s p) $ gridMap s
bmu = f ds
f [] = error "DSOM has no models"
f xs = fst $ minimumBy (comparing snd) xs
instance
(GM.GridMap gm p, k ~ G.Index (GM.BaseGrid gm p),
G.FiniteGrid (gm p), GM.GridMap gm x, k ~ G.Index (gm p),
k ~ G.Index (gm x), k ~ G.Index (GM.BaseGrid gm x), Ord k, Ord x,
Num x, Fractional x) =>
Classifier (DSOM gm) x k p where
toList = GM.toList . gridMap
numModels = G.tileCount . gridMap
models = GM.elems . gridMap
differences s p = GM.toList . GM.map (difference s p) $ gridMap s
trainBatch s = foldl' justTrain s
reportAndTrain s p = (bmu, ds, s')
where ds = differences s p
bmu = f ds
f [] = error "DSOM has no models"
f xs = fst $ minimumBy (comparing snd) xs
s' = trainNeighbourhood s bmu p
rougierLearningFunction
:: (Eq a, Ord a, Floating a) => a -> a -> (a -> a -> a -> a)
rougierLearningFunction r p bmuDiff diff dist
| bmuDiff == 0 = 0
| otherwise = r * abs diff * exp (-k*k)
where k = dist/(p*abs bmuDiff)