packages feed

som 5.1 → 6.4

raw patch · 8 files changed

+332/−102 lines, 8 filesdep ~gridPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: grid

API changes (from Hackage documentation)

+ Data.Datamining.Clustering.DSOM: customDSOM :: gm p -> (Metric p -> Metric p -> Metric p -> Metric p) -> DSOM gm k p
+ Data.Datamining.Clustering.DSOM: data DSOM gm k p
+ Data.Datamining.Clustering.DSOM: defaultDSOM :: (Eq (Metric p), Ord (Metric p), Floating (Metric p)) => gm p -> Metric p -> Metric p -> DSOM gm k p
+ Data.Datamining.Clustering.DSOM: rougierLearningFunction :: (Eq a, Ord a, Floating a) => a -> a -> (a -> a -> a -> a)
+ Data.Datamining.Clustering.DSOM: toGridMap :: GridMap gm p => DSOM gm k p -> gm p
+ Data.Datamining.Clustering.DSOM: trainNeighbourhood :: (Pattern p, FiniteGrid (gm p), GridMap gm p, Num (Metric p), Ord k, k ~ Index (gm p), k ~ Index (BaseGrid gm p), Fractional (Metric p)) => DSOM gm t p -> k -> p -> DSOM gm k p
+ Data.Datamining.Clustering.DSOMInternal: DSOM :: gm p -> (Metric p -> Metric p -> Metric p -> Metric p) -> DSOM gm k p
+ Data.Datamining.Clustering.DSOMInternal: adjustNode :: (Pattern p, FiniteGrid (gm p), GridMap gm p, k ~ Index (gm p), Ord k, k ~ Index (BaseGrid gm p), Num (Metric p), Fractional (Metric p)) => gm p -> (Metric p -> Metric p -> Metric p) -> p -> k -> k -> p -> p
+ Data.Datamining.Clustering.DSOMInternal: customDSOM :: gm p -> (Metric p -> Metric p -> Metric p -> Metric p) -> DSOM gm k p
+ Data.Datamining.Clustering.DSOMInternal: data DSOM gm k p
+ Data.Datamining.Clustering.DSOMInternal: defaultDSOM :: (Eq (Metric p), Ord (Metric p), Floating (Metric p)) => gm p -> Metric p -> Metric p -> DSOM gm k p
+ Data.Datamining.Clustering.DSOMInternal: instance (Foldable gm, GridMap gm p, FiniteGrid (BaseGrid gm p)) => GridMap (DSOM gm k) p
+ Data.Datamining.Clustering.DSOMInternal: instance (GridMap gm p, k ~ Index (BaseGrid gm p), Pattern p, FiniteGrid (gm p), GridMap gm (Metric p), k ~ Index (gm p), k ~ Index (BaseGrid gm (Metric p)), Ord k, Ord (Metric p), Num (Metric p), Fractional (Metric p)) => Classifier (DSOM gm) k p
+ Data.Datamining.Clustering.DSOMInternal: instance Foldable gm => Foldable (DSOM gm k)
+ Data.Datamining.Clustering.DSOMInternal: instance Grid (gm p) => Grid (DSOM gm k p)
+ Data.Datamining.Clustering.DSOMInternal: justTrain :: (Pattern p, FiniteGrid (gm p), GridMap gm p, Num (Metric p), Ord (Metric p), Ord (Index (gm p)), GridMap gm (Metric p), Fractional (Metric p), Index (BaseGrid gm (Metric p)) ~ Index (gm p), Index (BaseGrid gm p) ~ Index (gm p)) => DSOM gm t p -> p -> DSOM gm (Index (gm p)) p
+ Data.Datamining.Clustering.DSOMInternal: rougierLearningFunction :: (Eq a, Ord a, Floating a) => a -> a -> (a -> a -> a -> a)
+ Data.Datamining.Clustering.DSOMInternal: sGridMap :: DSOM gm k p -> gm p
+ Data.Datamining.Clustering.DSOMInternal: sLearningFunction :: DSOM gm k p -> (Metric p -> Metric p -> Metric p -> Metric p)
+ Data.Datamining.Clustering.DSOMInternal: scaleDistance :: (Num a, Fractional a) => Int -> Int -> a
+ Data.Datamining.Clustering.DSOMInternal: toGridMap :: GridMap gm p => DSOM gm k p -> gm p
+ Data.Datamining.Clustering.DSOMInternal: trainNeighbourhood :: (Pattern p, FiniteGrid (gm p), GridMap gm p, Num (Metric p), Ord k, k ~ Index (gm p), k ~ Index (BaseGrid gm p), Fractional (Metric p)) => DSOM gm t p -> k -> p -> DSOM gm k p
+ Data.Datamining.Clustering.SOM: counter :: SOM gm k p -> Int
+ Data.Datamining.Clustering.SOM: setCounter :: Int -> SOM gm k p -> SOM gm k p
+ Data.Datamining.Clustering.SOMInternal: adjustNode :: (Pattern p, Grid g, k ~ Index g) => g -> (Int -> Metric p) -> p -> k -> k -> p -> p
+ Data.Datamining.Clustering.SOMInternal: counter :: SOM gm k p -> Int
+ Data.Datamining.Clustering.SOMInternal: currentLearningFunction :: SOM gm k p -> (Int -> Metric p)
+ Data.Datamining.Clustering.SOMInternal: instance Constructor C1_0SOM
+ Data.Datamining.Clustering.SOMInternal: instance Datatype D1SOM
+ Data.Datamining.Clustering.SOMInternal: instance Generic (SOM gm k p)
+ Data.Datamining.Clustering.SOMInternal: instance Selector S1_0_0SOM
+ Data.Datamining.Clustering.SOMInternal: instance Selector S1_0_1SOM
+ Data.Datamining.Clustering.SOMInternal: instance Selector S1_0_2SOM
+ Data.Datamining.Clustering.SOMInternal: justTrain :: (Ord (Metric p), Pattern p, Grid (gm p), GridMap gm (Metric p), GridMap gm p, Index (BaseGrid gm (Metric p)) ~ Index (gm p), Index (BaseGrid gm p) ~ Index (gm p)) => SOM gm k p -> p -> SOM gm k p
+ Data.Datamining.Clustering.SOMInternal: setCounter :: Int -> SOM gm k p -> SOM gm k p
- Data.Datamining.Clustering.SOM: defaultSOM :: Floating (Metric p) => gm p -> Metric p -> Metric p -> Int -> SOM gm k p
+ Data.Datamining.Clustering.SOM: defaultSOM :: Floating (Metric p) => gm p -> Metric p -> Metric p -> Metric p -> Metric p -> Int -> SOM gm k p
- Data.Datamining.Clustering.SOMInternal: defaultSOM :: Floating (Metric p) => gm p -> Metric p -> Metric p -> Int -> SOM gm k p
+ Data.Datamining.Clustering.SOMInternal: defaultSOM :: Floating (Metric p) => gm p -> Metric p -> Metric p -> Metric p -> Metric p -> Int -> SOM gm k p

Files

som.cabal view
@@ -1,5 +1,5 @@ name:           som-version:        5.1+version:        6.4 synopsis:       Self-Organising Maps description:    A Kohonen Self-organising Map (SOM) maps input patterns                  onto a regular grid (usually two-dimensional) where each@@ -31,11 +31,13 @@                    base-unicode-symbols ==0.2.*,                    binary == 0.5.* || == 0.6.* || == 0.7.*,                    containers ==0.4.2.* || ==0.5.*,-                   grid >=6.1 && ==6.*,+                   grid ==7.*,                    MonadRandom ==0.1.*   ghc-options:     -Wall   exposed-modules: Data.Datamining.Clustering.SOM,                    Data.Datamining.Clustering.SOMInternal,+                   Data.Datamining.Clustering.DSOM,+                   Data.Datamining.Clustering.DSOMInternal,                    Data.Datamining.Clustering.Classifier,                    Data.Datamining.Pattern @@ -46,7 +48,7 @@                    QuickCheck ==2.5.* || ==2.6.*,                    test-framework == 0.8.*,                    som,-                   grid >=6.1 && ==6.*,+                   grid ==7.*,                    base-unicode-symbols ==0.2.*,                    MonadRandom ==0.1.*,                    random ==1.0.*
src/Data/Datamining/Clustering/Classifier.hs view
@@ -10,8 +10,7 @@ -- Tools for identifying patterns in data. -- -------------------------------------------------------------------------{-# LANGUAGE UnicodeSyntax, TypeFamilies, FlexibleContexts,-    MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies, FlexibleContexts, MultiParamTypeClasses #-} module Data.Datamining.Clustering.Classifier   (     Classifier(..)@@ -23,37 +22,37 @@  -- | A machine which learns to classify input patterns.  --   Minimal complete definition: @trainBatch@, @reportAndTrain@.-class Classifier (c ∷ * → * → *) k p where+class Classifier (c :: * -> * -> *) k p where   -- | Returns a list of index\/model pairs.-  toList ∷ c k p → [(k, p)]+  toList :: c k p -> [(k, p)]    -- | Returns the number of models this classifier can learn.-  numModels ∷ c k p → Int+  numModels :: c k p -> Int    -- | Returns the current models of the classifier.-  models ∷ c k p → [p]+  models :: c k p -> [p]    -- | @'differences' c target@ returns the indices of all nodes in    --   @c@, paired with the difference between @target@ and the    --   node's model.-  differences ∷ (Pattern p, v ~ Metric p) ⇒ c k p → p → [(k, v)]+  differences :: (Pattern p, v ~ Metric p) => c k p -> p -> [(k, v)]    -- | @classify c target@ returns the index of the node in @c@    --   whose model best matches the @target@.-  classify ∷ (Pattern p, Ord v, v ~ Metric p) ⇒ c k p → p → k+  classify :: (Pattern p, Ord v, v ~ Metric p) => c k p -> p -> k   classify c p = fst . minimumBy (comparing snd) $ differences c p    -- | @'train' c target@ returns a modified copy   --   of the classifier @c@ that has partially learned the @target@.   train-    ∷ (Ord v, v ~ Metric p) ⇒ -      c k p → p → c k p+    :: (Ord v, v ~ Metric p) => +      c k p -> p -> c k p   train c p = c'     where (_, _, c') = reportAndTrain c p    -- | @'trainBatch' c targets@ returns a modified copy   --   of the classifier @c@ that has partially learned the @targets@.-  trainBatch ∷ c k p → [p] → c k p+  trainBatch :: c k p -> [p] -> c k p    -- | @'classifyAndTrain' c target@ returns a tuple containing the   --   index of the node in @c@ whose model best matches the input@@ -63,8 +62,8 @@   --   they   --   should give identical results.   classifyAndTrain -    ∷ (Ord v, v ~ Metric p) ⇒ -      c k p → p → (k, c k p)+    :: (Ord v, v ~ Metric p) => +      c k p -> p -> (k, c k p)   classifyAndTrain c p = (bmu, c')     where (bmu, _, c') = reportAndTrain c p @@ -77,8 +76,8 @@   --   @(p `diff` c, train c p)@, but they should give identical   --   results.   diffAndTrain-    ∷ (Ord v, v ~ Metric p) ⇒ -      c k p → p → ([(k, v)], c k p)+    :: (Ord v, v ~ Metric p) => +      c k p -> p -> ([(k, v)], c k p)   diffAndTrain c p = (ds, c')     where (_, ds, c') = reportAndTrain c p @@ -93,7 +92,7 @@   --   @(p `diff` c, train c p)@, but they should give identical   --   results.   reportAndTrain -    ∷ (Ord v, v ~ Metric p) ⇒ -      c k p → p → (k, [(k, v)], c k p)+    :: (Ord v, v ~ Metric p) => +      c k p -> p -> (k, [(k, v)], c k p)  
+ src/Data/Datamining/Clustering/DSOM.hs view
@@ -0,0 +1,35 @@+------------------------------------------------------------------------+-- |+-- Module      :  Data.Datamining.Clustering.SOM+-- Copyright   :  (c) Amy de Buitléir 2012-2013+-- License     :  BSD-style+-- Maintainer  :  amy@nualeargais.ie+-- Stability   :  experimental+-- Portability :  portable+--+-- A modified Kohonen Self-organising Map (SOM) which supports a+-- time-independent learning function. (See+-- @'Data.Datamining.Clustering.SOM'@ for a description of a SOM.)+--+-- References:+--+-- * Rougier, N. & Boniface, Y. (2011). Dynamic self-organising map.+--   Neurocomputing, 74 (11), 1840-1847. +------------------------------------------------------------------------++module Data.Datamining.Clustering.DSOM+  (+    -- * Construction+    DSOM,+    defaultDSOM,+    customDSOM,+    rougierLearningFunction,+    -- * Deconstruction+    toGridMap,+    -- * Advanced control+    trainNeighbourhood+  ) where++import Data.Datamining.Clustering.DSOMInternal (DSOM, defaultDSOM,+  customDSOM, rougierLearningFunction, toGridMap, trainNeighbourhood)+
+ src/Data/Datamining/Clustering/DSOMInternal.hs view
@@ -0,0 +1,198 @@+------------------------------------------------------------------------+-- |+-- Module      :  Data.Datamining.Clustering.DSOMInternal+-- Copyright   :  (c) Amy de Buitléir 2012-2013+-- License     :  BSD-style+-- Maintainer  :  amy@nualeargais.ie+-- Stability   :  experimental+-- Portability :  portable+--+-- A module containing private @DSOM@ internals. Most developers should+-- use @DSOM@ instead. This module is subject to change without notice.+--+------------------------------------------------------------------------+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances,+    MultiParamTypeClasses #-}++module Data.Datamining.Clustering.DSOMInternal where++import qualified Data.Foldable as F (Foldable, foldr)+import Data.List (foldl', minimumBy)+import Data.Ord (comparing)+import qualified Math.Geometry.Grid as G (Grid(..), FiniteGrid(..))+import qualified Math.Geometry.GridMap as GM (GridMap(..))+import Data.Datamining.Pattern (Pattern(..))+import Data.Datamining.Clustering.Classifier(Classifier(..))+import Prelude hiding (lookup)++-- | A Self-Organising Map (DSOM).+--+--   Although @DSOM@ implements @GridMap@, most users will only need the+--   interface provided by @Data.Datamining.Clustering.Classifier@. If+--   you chose to use the @GridMap@ functions, please note:+--+--   1. The functions @adjust@, and @adjustWithKey@ do not increment the+--      counter. You can do so manually with @incrementCounter@.+--+--   2. The functions @map@ and @mapWithKey@ are not implemented (they+--      just return an @error@). It would be problematic to implement+--      them because the input DSOM and the output DSOM would have to+--      have the same @Metric@ type.+data DSOM gm k p = DSOM+  {+    sGridMap :: gm p,+    sLearningFunction :: (Metric p -> Metric p -> Metric p -> Metric p)+  }++instance (F.Foldable gm) => F.Foldable (DSOM gm k) where+  foldr f x g = F.foldr f x (sGridMap g)++instance (G.Grid (gm p)) => G.Grid (DSOM gm k p) where+  type Index (DSOM gm k p) = G.Index (gm p)+  type Direction (DSOM gm k p) = G.Direction (gm p)+  indices = G.indices . sGridMap+  distance = G.distance . sGridMap+  neighbours = G.neighbours . sGridMap+  contains = G.contains . sGridMap+  viewpoint = G.viewpoint . sGridMap+  directionTo = G.directionTo . sGridMap+  tileCount = G.tileCount . sGridMap+  null = G.null . sGridMap+  nonNull = G.nonNull . sGridMap++instance+  (F.Foldable gm, GM.GridMap gm p, G.FiniteGrid (GM.BaseGrid gm p)) =>+    GM.GridMap (DSOM gm k) p where+  type BaseGrid (DSOM gm k) p = GM.BaseGrid gm p+  toGrid = GM.toGrid . sGridMap+  toMap = GM.toMap . sGridMap+  mapWithKey = error "Not implemented"+  adjustWithKey f k s = s { sGridMap=gm' }+    where gm = sGridMap s+          gm' = GM.adjustWithKey f k gm++-- | Extracts the grid and current models from the DSOM.+toGridMap :: GM.GridMap gm p => DSOM gm k p -> gm p+toGridMap = sGridMap++adjustNode+  :: (Pattern p, G.FiniteGrid (gm p), GM.GridMap gm p,+      k ~ G.Index (gm p), Ord k, k ~ G.Index (GM.BaseGrid gm p),+      Num (Metric p), Fractional (Metric p)) => +     gm p -> (Metric p -> Metric p -> Metric p) -> p -> k -> k -> p -> p+adjustNode gm f target bmu k = makeSimilar target amount+  where diff = difference (gm GM.! k) target+        dist = scaleDistance (G.distance gm bmu k)+                 (G.maxPossibleDistance gm)+        amount = f diff dist++scaleDistance :: (Num a, Fractional a) => Int -> Int -> a+scaleDistance d dMax+  | dMax == 0  = 0+  | otherwise = fromIntegral d / fromIntegral dMax++-- | Trains the specified node and the neighbourood around it to better+--   match a target.+--   Most users should use @train@, which automatically determines+--   the BMU and trains it and its neighbourhood.+trainNeighbourhood+  :: (Pattern p, G.FiniteGrid (gm p), GM.GridMap gm p, Num (Metric p),+      Ord k, k ~ G.Index (gm p),+      k ~ G.Index (GM.BaseGrid gm p), Fractional (Metric p)) =>+     DSOM gm t p -> k -> p -> DSOM gm k p+trainNeighbourhood s bmu target = s { sGridMap=gm' }+  where gm = sGridMap s+        gm' = GM.mapWithKey (adjustNode gm f target bmu) gm+        f = (sLearningFunction s) bmuDiff+        bmuDiff = difference (gm GM.! bmu) target++justTrain+  :: (Pattern p, G.FiniteGrid (gm p), GM.GridMap gm p,+      Num (Metric p), Ord (Metric p), Ord (G.Index (gm p)),+      GM.GridMap gm (Metric p), Fractional (Metric p),+      G.Index (GM.BaseGrid gm (Metric p)) ~ G.Index (gm p),+      G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) =>+     DSOM gm t p -> p -> DSOM gm (G.Index (gm p)) p+justTrain s p = trainNeighbourhood s bmu p+  where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s+        bmu = fst . minimumBy (comparing snd) $ ds++instance+  (GM.GridMap gm p, k ~ G.Index (GM.BaseGrid gm p), Pattern p,+    G.FiniteGrid (gm p), GM.GridMap gm (Metric p), k ~ G.Index (gm p),+    k ~ G.Index (GM.BaseGrid gm (Metric p)), Ord k, Ord (Metric p),+    Num (Metric p), Fractional (Metric p)) =>+   Classifier (DSOM gm) k p where+  toList = GM.toList . sGridMap+  numModels = G.tileCount . sGridMap+  models = GM.elems . sGridMap+  differences s p = GM.toList . GM.map (p `difference`) . sGridMap $ s+  trainBatch s = foldl' justTrain s+  reportAndTrain s p = (bmu, ds, s')+    where ds = differences s p+          bmu = fst . minimumBy (comparing snd) $ ds+          s' = trainNeighbourhood s bmu p+++-- | Creates a classifier with a default (bell-shaped) learning+--   function. Usage is @'defaultDSOM' gm r w t@, where:+--+--   [@gm@] The geometry and initial models for this classifier.+--   A reasonable choice here is @'lazyGridMap' g ps@, where @g@ is a+--   @'HexHexGrid'@, and @ps@ is a set of random patterns.+--+--   [@r@] and [@p@] are the first two parameters to the+--   @'rougierLearningFunction'@.+defaultDSOM+  :: (Eq (Metric p), Ord (Metric p), Floating (Metric p)) =>+     gm p -> Metric p -> Metric p -> DSOM gm k p+defaultDSOM gm r p =+  DSOM {+        sGridMap=gm,+        sLearningFunction=rougierLearningFunction r p+      }++-- | Creates a classifier with a custom learning function.+--   Usage is @'customDSOM' gm g@, where:+--+--   [@gm@] The geometry and initial models for this classifier.+--   A reasonable choice here is @'lazyGridMap' g ps@, where @g@ is a+--   @'HexHexGrid'@, and @ps@ is a set of random patterns.+--+--   [@f@] A function used to determine the learning rate (for+--   adjusting the models in the classifier).+--   This function will be invoked with three parameters.+--   The first parameter will indicate how different the BMU is from+--   the input pattern.+--   The second parameter indicates how different the pattern of the+--   node currently being trained is from the input pattern.+--   The third parameter is the grid distance from the BMU to the node+--   currently being trained, as a fraction of the maximum grid+--   distance.+--   The output is the learning rate for that node (the amount by+--   which the node's model should be updated to match the target).+--   The learning rate should be between zero and one.+customDSOM+  :: gm p -> (Metric p -> Metric p -> Metric p -> Metric p) -> DSOM gm k p+customDSOM gm f =+  DSOM {+        sGridMap=gm,+        sLearningFunction=f+      }++-- | Configures a learning function that depends not on the time, but+--   on how good a model we already have for the target. If the+--   BMU is an exact match for the target, no learning occurs.+--   Usage is @'rougierLearningFunction' r p@, where @r@ is the+--   maximal learning rate (0 <= r <= 1), and @p@ is the elasticity.+--+--   NOTE: When using this learning function, ensure that+--   @abs . difference@ is always between 0 and 1, inclusive. Otherwise+--   you may get invalid learning rates.+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) +
src/Data/Datamining/Clustering/SOM.hs view
@@ -35,9 +35,10 @@ -- * Kohonen, T. (1982). Self-organized formation of topologically  --   correct feature maps. Biological Cybernetics, 43 (1), 59–69. --+-- * Rougier, N. & Boniface, Y. (2011). Dynamic self-organising map.+--   Neurocomputing, 74 (11), 1840-1847.  ------------------------------------------------------------------------ -{-# LANGUAGE UnicodeSyntax #-} module Data.Datamining.Clustering.SOM   (     -- * Construction@@ -49,10 +50,12 @@     toGridMap,     -- * Advanced control     trainNeighbourhood,-    incrementCounter+    incrementCounter,+    counter,+    setCounter   ) where  import Data.Datamining.Clustering.SOMInternal (SOM, defaultSOM,   customSOM, decayingGaussian, toGridMap, trainNeighbourhood,-  incrementCounter)+  incrementCounter, counter, setCounter) 
src/Data/Datamining/Clustering/SOMInternal.hs view
@@ -11,23 +11,10 @@ -- use @SOM@ instead. This module is subject to change without notice. -- -------------------------------------------------------------------------{-# LANGUAGE UnicodeSyntax, TypeFamilies, FlexibleContexts,-    FlexibleInstances, MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances,+    MultiParamTypeClasses, DeriveGeneric #-} -module Data.Datamining.Clustering.SOMInternal-  (-    -- * Construction-    SOM(..),-    defaultSOM,-    customSOM,-    decayingGaussian,-    decayingGaussian2,-    -- * Deconstruction-    toGridMap,-    -- * Advanced control-    trainNeighbourhood,-    incrementCounter-  ) where+module Data.Datamining.Clustering.SOMInternal where  import qualified Data.Foldable as F (Foldable, foldr) import Data.List (foldl', minimumBy)@@ -36,6 +23,7 @@ import qualified Math.Geometry.GridMap as GM (GridMap(..)) import Data.Datamining.Pattern (Pattern(..)) import Data.Datamining.Clustering.Classifier(Classifier(..))+import GHC.Generics (Generic) import Prelude hiding (lookup)  -- | A Self-Organising Map (SOM).@@ -53,15 +41,15 @@ --      the same @Metric@ type. data SOM gm k p = SOM   {-    sGridMap ∷ gm p,-    sLearningFunction ∷ Int → Int → Metric p,-    sCounter ∷ Int-  }+    sGridMap :: gm p,+    sLearningFunction :: Int -> Int -> Metric p,+    sCounter :: Int+  } deriving Generic -instance (F.Foldable gm) ⇒ F.Foldable (SOM gm k) where+instance (F.Foldable gm) => F.Foldable (SOM gm k) where   foldr f x g = F.foldr f x (sGridMap g) -instance (G.Grid (gm p)) ⇒ G.Grid (SOM gm k p) where+instance (G.Grid (gm p)) => G.Grid (SOM gm k p) where   type Index (SOM gm k p) = G.Index (gm p)   type Direction (SOM gm k p) = G.Direction (gm p)   indices = G.indices . sGridMap@@ -74,7 +62,7 @@   null = G.null . sGridMap   nonNull = G.nonNull . sGridMap -instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p)) ⇒ GM.GridMap (SOM gm k) p where+instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p)) => GM.GridMap (SOM gm k) p where   type BaseGrid (SOM gm k) p = GM.BaseGrid gm p   toGrid = GM.toGrid . sGridMap   toMap = GM.toMap . sGridMap@@ -83,16 +71,16 @@     where gm = sGridMap s           gm' = GM.adjustWithKey f k gm -currentLearningFunction ∷ SOM gm k p → (Int → Metric p)+currentLearningFunction :: SOM gm k p -> (Int -> Metric p) currentLearningFunction s = (sLearningFunction s) (sCounter s)  -- | Extracts the grid and current models from the SOM.-toGridMap ∷ GM.GridMap gm p ⇒ SOM gm k p → gm p+toGridMap :: GM.GridMap gm p => SOM gm k p -> gm p toGridMap = sGridMap  adjustNode-  ∷ (Pattern p, G.Grid g, k ~ G.Index g) ⇒-     g → (Int → Metric p) → p → k → k → p → p+  :: (Pattern p, G.Grid g, k ~ G.Index g) =>+     g -> (Int -> Metric p) -> p -> k -> k -> p -> p adjustNode g f target bmu k = makeSimilar target (f d)   where d = G.distance g bmu k @@ -101,23 +89,29 @@ --   Most users should use @train@, which automatically determines --   the BMU and trains it and its neighbourhood. trainNeighbourhood-  ∷ (Pattern p, G.Grid (gm p), GM.GridMap gm p,-      G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) ⇒-     SOM gm k p → G.Index (gm p) → p → SOM gm k p+  :: (Pattern p, G.Grid (gm p), GM.GridMap gm p,+      G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) =>+     SOM gm k p -> G.Index (gm p) -> p -> SOM gm k p trainNeighbourhood s bmu target = s { sGridMap=gm' }   where gm = sGridMap s         gm' = GM.mapWithKey (adjustNode gm f target bmu) gm         f = currentLearningFunction s -incrementCounter :: SOM gm k p → SOM gm k p-incrementCounter s = s { sCounter=sCounter s + 1}+incrementCounter :: SOM gm k p -> SOM gm k p+incrementCounter s = setCounter (sCounter s + 1) s +counter :: SOM gm k p -> Int+counter = sCounter++setCounter :: Int -> SOM gm k p -> SOM gm k p+setCounter k s = s { sCounter = k }+ justTrain-  ∷ (Ord (Metric p), Pattern p, G.Grid (gm p),+  :: (Ord (Metric p), Pattern p, G.Grid (gm p),       GM.GridMap gm (Metric p), GM.GridMap gm p,       G.Index (GM.BaseGrid gm (Metric p)) ~ G.Index (gm p),-      G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) ⇒-     SOM gm k p → p → SOM gm k p+      G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) =>+     SOM gm k p -> p -> SOM gm k p justTrain s p = trainNeighbourhood s bmu p   where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s         bmu = fst . minimumBy (comparing snd) $ ds@@ -125,7 +119,7 @@ instance   (GM.GridMap gm p, k ~ G.Index (GM.BaseGrid gm p), Pattern p,   G.Grid (gm p), GM.GridMap gm (Metric p), k ~ G.Index (gm p),-  k ~ G.Index (GM.BaseGrid gm (Metric p)), Ord (Metric p)) ⇒+  k ~ G.Index (GM.BaseGrid gm (Metric p)), Ord (Metric p)) =>     Classifier (SOM gm) k p where   toList = GM.toList . sGridMap   numModels = G.tileCount . sGridMap@@ -139,29 +133,28 @@   -- | Creates a classifier with a default (bell-shaped) learning---   function. Usage is @'defaultSOM' gm r w t@, where:+--   function. Usage is @'defaultSOM' gm r0 rf w0 wf tf@, where: -- --   [@gm@] The geometry and initial models for this classifier. --   A reasonable choice here is @'lazyGridMap' g ps@, where @g@ is a --   @'HexHexGrid'@, and @ps@ is a set of random patterns. -----   [@r@] The learning rate to be applied to the BMU (Best Matching Unit)---   at "time" zero. The BMU is the model which best matches the---   current target pattern.+--   [@r0@] See description in @'decayingGaussian2'@. -----   [@w@] The width of the bell curve at time zero.+--   [@rf@] See description in @'decayingGaussian2'@. -----   [@t@] Controls how rapidly the learning rate decays. After this---   time, any learning done by the classifier will be negligible.---   We recommend setting this parameter to the number of patterns---   (or pattern batches) that will be presented to the classifier. An---   estimate is fine.+--   [@w0@] See description in @'decayingGaussian2'@.+--+--   [@wf@] See description in @'decayingGaussian2'@.+--+--   [@tf@] See description in @'decayingGaussian2'@. defaultSOM-  ∷ Floating (Metric p) ⇒ gm p → Metric p → Metric p → Int → SOM gm k p-defaultSOM gm r w t =+  :: Floating (Metric p) => gm p -> Metric p -> Metric p -> Metric p ->+     Metric p -> Int -> SOM gm k p+defaultSOM gm r0 rf w0 wf tf =   SOM {         sGridMap=gm,-        sLearningFunction=decayingGaussian r w t,+        sLearningFunction=decayingGaussian2 r0 rf w0 wf tf,         sCounter=0       } @@ -182,7 +175,7 @@ --   The output is the learning rate for that node (the amount by --   which the node's model should be updated to match the target). --   The learning rate should be between zero and one.-customSOM ∷ gm p → (Int → Int → Metric p) → SOM gm k p+customSOM :: gm p -> (Int -> Int -> Metric p) -> SOM gm k p customSOM gm f =   SOM {         sGridMap=gm,@@ -196,7 +189,7 @@ --   BMU) is @r0@, and the neighbourhood width is @w0@. Over time the --   neighbourhood width shrinks and the learning rate tapers off. decayingGaussian-  ∷ Floating a ⇒ a → a → Int → (Int → Int → a)+  :: Floating a => a -> a -> Int -> (Int -> Int -> a) decayingGaussian r w0 tMax t d = r * s * exp (-(d'*d')/(2*w0*w0*s*s))     where s = exp (-t'/tMax')           t' = fromIntegral t@@ -218,7 +211,7 @@ -- --   where << means "is much smaller than" (not the Haskell @<<@ --   operator!) -decayingGaussian2 :: Floating a ⇒ a → a → a → a → Int → (Int → Int → a)+decayingGaussian2 :: Floating a => a -> a -> a -> a -> Int -> (Int -> Int -> a) decayingGaussian2 r0 rf w0 wf tf t d = r * exp (-(d'*d')/(w*w))     where a = t'/tf'           r = r0 * ((rf/r0)**a)
src/Data/Datamining/Pattern.hs view
@@ -10,8 +10,7 @@ -- Tools for identifying patterns in data. -- -------------------------------------------------------------------------{-# LANGUAGE UnicodeSyntax, TypeFamilies, FlexibleContexts,-    MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies, FlexibleContexts, MultiParamTypeClasses #-} module Data.Datamining.Pattern   (     -- * Patterns@@ -35,7 +34,6 @@     scaleAll   ) where -import Data.Eq.Unicode ((≡)) import Data.List (foldl')  -- | A pattern to be learned or classified.@@ -44,7 +42,7 @@   -- | Compares two patterns and returns a /non-negative/ number   --   representing how different the patterns are. A result of @0@   --   indicates that the patterns are identical.-  difference ∷ p → p → Metric p+  difference :: p -> p -> Metric p   -- | @'makeSimilar' target amount pattern@ returns a modified copy of   --   @pattern@ that is more similar to @target@ than @pattern@ is. The   --   magnitude of the adjustment is controlled by the @amount@@@ -52,7 +50,7 @@   --   values for @amount@ permit greater adjustments. If @amount@=1,   --   the result should be identical to the @target@. If @amount@=0,   --   the result should be the unmodified @pattern@.-  makeSimilar ∷ p → Metric p → p → p+  makeSimilar :: p -> Metric p -> p -> p  -- -- Using numbers as patterns.@@ -65,7 +63,7 @@ adjustNum target r x   | r < 0     = error "Negative learning rate"   | r > 1     = error "Learning rate > 1"-  | r ≡ 1     = x+  | r == 1     = x   | otherwise = adjustNum' r target x  -- Note that parameters are swapped@@ -76,22 +74,22 @@ If you wish to use, say, a @Double@ as a pattern, one option is to use @no-warn-orphans@ and add the following to your code: -> instance Double ⇒ Pattern Double where+> instance Double => Pattern Double where >   type Metric Double = Double->   difference = euclideanDistanceSquared->   makeSimilar = adjustVector+>   difference = absDifference+>   makeSimilar = adjustNum -}  -- -- Using numeric vectors as patterns. -- -magnitudeSquared ∷ Num a ⇒ [a] → a-magnitudeSquared xs =  sum $ map (\x → x*x) xs+magnitudeSquared :: Num a => [a] -> a+magnitudeSquared xs =  sum $ map (\x -> x*x) xs  -- | Calculates the square of the Euclidean distance between two --   vectors.-euclideanDistanceSquared ∷ Num a ⇒ [a] → [a] → a+euclideanDistanceSquared :: Num a => [a] -> [a] -> a euclideanDistanceSquared xs ys = magnitudeSquared $ zipWith (-) xs ys  -- | @'adjustVector' target amount vector@ adjusts @vector@ to move it@@ -100,11 +98,11 @@ --   of @r@ permit more adjustment. If @r@=1, the result will be --   identical to the @target@. If @amount@=0, the result will be the --   unmodified @pattern@.-adjustVector ∷ (Num a, Ord a, Eq a) ⇒ [a] → a → [a] → [a]+adjustVector :: (Num a, Ord a, Eq a) => [a] -> a -> [a] -> [a] adjustVector xs r ys   | r < 0     = error "Negative learning rate"   | r > 1     = error "Learning rate > 1"-  | r ≡ 1     = xs+  | r == 1     = xs   | otherwise = zipWith (adjustNum' r) xs ys  -- | A vector that has been normalised, i.e., the magnitude of the@@ -112,15 +110,15 @@ data NormalisedVector a = NormalisedVector [a] deriving Show  -- | Normalises a vector-normalise ∷ Floating a ⇒ [a] → NormalisedVector a+normalise :: Floating a => [a] -> NormalisedVector a normalise xs = NormalisedVector $ map (/x) xs   where x = norm xs -norm ∷ Floating a ⇒ [a] → a+norm :: Floating a => [a] -> a norm xs = sqrt $ sum (map f xs)   where f x = x*x -instance (Floating a, Fractional a, Ord a, Eq a) ⇒+instance (Floating a, Fractional a, Ord a, Eq a) =>     Pattern (NormalisedVector a) where   type Metric (NormalisedVector a) = a   difference (NormalisedVector xs) (NormalisedVector ys) =@@ -140,28 +138,28 @@ --   @xs@, @'scale' qs xs@ scales the vector @xs@ element by element, --   mapping the maximum value expected at that index to one, and the --   minimum value to zero.-scale ∷ Fractional a ⇒ [(a,a)] → [a] → ScaledVector a+scale :: Fractional a => [(a,a)] -> [a] -> ScaledVector a scale qs xs = ScaledVector $ zipWith scaleValue qs xs  -- | Scales a set of vectors by determining the maximum and minimum --   values at each index in the vector, and mapping the maximum --   value to one, and the minimum value to zero.-scaleAll ∷ (Fractional a, Ord a) ⇒ [[a]] → [ScaledVector a]+scaleAll :: (Fractional a, Ord a) => [[a]] -> [ScaledVector a] scaleAll xss = map (scale qs) xss   where qs = quantify xss -scaleValue ∷ Fractional a ⇒ (a,a) → a → a+scaleValue :: Fractional a => (a,a) -> a -> a scaleValue (minX,maxX) x = (x - minX) / (maxX-minX) -quantify ∷ Ord a ⇒ [[a]] → [(a,a)]+quantify :: Ord a => [[a]] -> [(a,a)] quantify xss = foldl' quantify' qs (tail xss)   where qs = zip (head xss) (head xss) -quantify' ∷ Ord a ⇒ [(a,a)] → [a] → [(a,a)]+quantify' :: Ord a => [(a,a)] -> [a] -> [(a,a)] quantify' = zipWith f   where f (minX, maxX) x = (min minX x, max maxX x) -instance (Fractional a, Ord a, Eq a) ⇒ Pattern (ScaledVector a) where+instance (Fractional a, Ord a, Eq a) => Pattern (ScaledVector a) where   type Metric (ScaledVector a) = a   difference (ScaledVector xs) (ScaledVector ys) =     euclideanDistanceSquared xs ys@@ -172,7 +170,7 @@ If you wish to use raw numeric vectors as a pattern, one option is to use @no-warn-orphans@ and add the following to your code: -> instance (Floating a, Fractional a, Ord a, Eq a) ⇒ Pattern [a] where+> instance (Floating a, Fractional a, Ord a, Eq a) => Pattern [a] where >   type Metric [a] = a >   difference = euclideanDistanceSquared >   makeSimilar = adjustVector
test/Main.hs view
@@ -3,15 +3,17 @@  import Data.Datamining.PatternQC ( test ) import Data.Datamining.Clustering.SOMQC ( test )+import Data.Datamining.Clustering.DSOMQC ( test )  import Test.Framework as TF ( defaultMain, Test ) -tests ∷ [TF.Test]+tests :: [TF.Test] tests =    [      Data.Datamining.PatternQC.test,-    Data.Datamining.Clustering.SOMQC.test+    Data.Datamining.Clustering.SOMQC.test,+    Data.Datamining.Clustering.DSOMQC.test   ] -main ∷ IO ()+main :: IO () main = defaultMain tests