packages feed

som 8.0.6 → 8.1.1

raw patch · 4 files changed

+506/−2 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Datamining.Clustering.DSOM: DSOM :: gm p -> (x -> x -> x -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> DSOM gm x k p
- Data.Datamining.Clustering.DSOMInternal: DSOM :: gm p -> (x -> x -> x -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> DSOM gm x k p
- Data.Datamining.Clustering.DSOMInternal: instance (Data.Foldable.Foldable gm, Math.Geometry.GridMap.GridMap gm p, Math.Geometry.GridInternal.FiniteGrid (Math.Geometry.GridMap.BaseGrid gm p)) => Math.Geometry.GridMap.GridMap (Data.Datamining.Clustering.DSOMInternal.DSOM gm x k) p
- Data.Datamining.Clustering.DSOMInternal: instance (Math.Geometry.GridMap.GridMap gm p, k ~ Math.Geometry.GridInternal.Index (Math.Geometry.GridMap.BaseGrid gm p), Math.Geometry.GridInternal.FiniteGrid (gm p), Math.Geometry.GridMap.GridMap gm x, k ~ Math.Geometry.GridInternal.Index (gm p), k ~ Math.Geometry.GridInternal.Index (gm x), k ~ Math.Geometry.GridInternal.Index (Math.Geometry.GridMap.BaseGrid gm x), GHC.Classes.Ord k, GHC.Classes.Ord x, GHC.Num.Num x, GHC.Real.Fractional x) => Data.Datamining.Clustering.Classifier.Classifier (Data.Datamining.Clustering.DSOMInternal.DSOM gm) x k p
- Data.Datamining.Clustering.DSOMInternal: instance Control.DeepSeq.NFData (gm p) => Control.DeepSeq.NFData (Data.Datamining.Clustering.DSOMInternal.DSOM gm x k p)
- Data.Datamining.Clustering.DSOMInternal: instance Data.Foldable.Foldable gm => Data.Foldable.Foldable (Data.Datamining.Clustering.DSOMInternal.DSOM gm x k)
- Data.Datamining.Clustering.DSOMInternal: instance GHC.Generics.Constructor Data.Datamining.Clustering.DSOMInternal.C1_0DSOM
- Data.Datamining.Clustering.DSOMInternal: instance GHC.Generics.Datatype Data.Datamining.Clustering.DSOMInternal.D1DSOM
- Data.Datamining.Clustering.DSOMInternal: instance GHC.Generics.Generic (Data.Datamining.Clustering.DSOMInternal.DSOM gm x k p)
- Data.Datamining.Clustering.DSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.DSOMInternal.S1_0_0DSOM
- Data.Datamining.Clustering.DSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.DSOMInternal.S1_0_1DSOM
- Data.Datamining.Clustering.DSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.DSOMInternal.S1_0_2DSOM
- Data.Datamining.Clustering.DSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.DSOMInternal.S1_0_3DSOM
- Data.Datamining.Clustering.DSOMInternal: instance Math.Geometry.GridInternal.Grid (gm p) => Math.Geometry.GridInternal.Grid (Data.Datamining.Clustering.DSOMInternal.DSOM gm x k p)
- Data.Datamining.Clustering.SOM: SOM :: gm p -> (t -> d -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SOM t d gm x k p
- Data.Datamining.Clustering.SOMInternal: SOM :: gm p -> (t -> d -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SOM t d gm x k p
- Data.Datamining.Clustering.SOMInternal: instance (Control.DeepSeq.NFData t, Control.DeepSeq.NFData (gm p)) => Control.DeepSeq.NFData (Data.Datamining.Clustering.SOMInternal.SOM t d gm x k p)
- Data.Datamining.Clustering.SOMInternal: instance (Data.Foldable.Foldable gm, Math.Geometry.GridMap.GridMap gm p, Math.Geometry.GridInternal.Grid (Math.Geometry.GridMap.BaseGrid gm p)) => Math.Geometry.GridMap.GridMap (Data.Datamining.Clustering.SOMInternal.SOM t d gm x k) p
- Data.Datamining.Clustering.SOMInternal: instance (Math.Geometry.GridMap.GridMap gm p, k ~ Math.Geometry.GridInternal.Index (Math.Geometry.GridMap.BaseGrid gm p), Math.Geometry.GridInternal.Grid (gm p), Math.Geometry.GridMap.GridMap gm x, k ~ Math.Geometry.GridInternal.Index (gm p), k ~ Math.Geometry.GridInternal.Index (Math.Geometry.GridMap.BaseGrid gm x), GHC.Num.Num t, GHC.Classes.Ord x, GHC.Num.Num x, GHC.Num.Num d) => Data.Datamining.Clustering.Classifier.Classifier (Data.Datamining.Clustering.SOMInternal.SOM t d gm) x k p
- Data.Datamining.Clustering.SOMInternal: instance Data.Foldable.Foldable gm => Data.Foldable.Foldable (Data.Datamining.Clustering.SOMInternal.SOM t d gm x k)
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Constructor Data.Datamining.Clustering.SOMInternal.C1_0SOM
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Datatype Data.Datamining.Clustering.SOMInternal.D1SOM
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Generic (Data.Datamining.Clustering.SOMInternal.SOM t d gm x k p)
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SOMInternal.S1_0_0SOM
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SOMInternal.S1_0_1SOM
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SOMInternal.S1_0_2SOM
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SOMInternal.S1_0_3SOM
- Data.Datamining.Clustering.SOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SOMInternal.S1_0_4SOM
- Data.Datamining.Clustering.SOMInternal: instance Math.Geometry.GridInternal.Grid (gm p) => Math.Geometry.GridInternal.Grid (Data.Datamining.Clustering.SOMInternal.SOM t d gm x k p)
- Data.Datamining.Clustering.SSOM: SSOM :: Map k p -> (t -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SSOM t x k p
- Data.Datamining.Clustering.SSOMInternal: SSOM :: Map k p -> (t -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SSOM t x k p
- Data.Datamining.Clustering.SSOMInternal: instance (Control.DeepSeq.NFData t, Control.DeepSeq.NFData k, Control.DeepSeq.NFData p) => Control.DeepSeq.NFData (Data.Datamining.Clustering.SSOMInternal.SSOM t x k p)
- Data.Datamining.Clustering.SSOMInternal: instance (GHC.Num.Num t, GHC.Classes.Ord x, GHC.Num.Num x, GHC.Classes.Ord k) => Data.Datamining.Clustering.Classifier.Classifier (Data.Datamining.Clustering.SSOMInternal.SSOM t) x k p
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Constructor Data.Datamining.Clustering.SSOMInternal.C1_0SSOM
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Datatype Data.Datamining.Clustering.SSOMInternal.D1SSOM
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Generic (Data.Datamining.Clustering.SSOMInternal.SSOM t x k p)
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SSOMInternal.S1_0_0SSOM
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SSOMInternal.S1_0_1SSOM
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SSOMInternal.S1_0_2SSOM
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SSOMInternal.S1_0_3SSOM
- Data.Datamining.Clustering.SSOMInternal: instance GHC.Generics.Selector Data.Datamining.Clustering.SSOMInternal.S1_0_4SSOM
- Data.Datamining.Pattern: instance GHC.Show.Show a => GHC.Show.Show (Data.Datamining.Pattern.NormalisedVector a)
- Data.Datamining.Pattern: instance GHC.Show.Show a => GHC.Show.Show (Data.Datamining.Pattern.ScaledVector a)
+ Data.Datamining.Clustering.DSOM: [DSOM] :: gm p -> (x -> x -> x -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> DSOM gm x k p
+ Data.Datamining.Clustering.DSOMInternal: [DSOM] :: gm p -> (x -> x -> x -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> DSOM gm x k p
+ Data.Datamining.Clustering.DSOMInternal: instance (Foldable gm, GridMap gm p, FiniteGrid (BaseGrid gm p)) => GridMap (DSOM gm x k) p
+ Data.Datamining.Clustering.DSOMInternal: instance (GridMap gm p, k ~ Index (BaseGrid gm p), FiniteGrid (gm p), GridMap gm x, k ~ Index (gm p), k ~ Index (gm x), k ~ Index (BaseGrid gm x), Ord k, Ord x, Num x, Fractional x) => Classifier (DSOM gm) x k p
+ Data.Datamining.Clustering.DSOMInternal: instance Constructor C1_0DSOM
+ Data.Datamining.Clustering.DSOMInternal: instance Datatype D1DSOM
+ Data.Datamining.Clustering.DSOMInternal: instance Foldable gm => Foldable (DSOM gm x k)
+ Data.Datamining.Clustering.DSOMInternal: instance Generic (DSOM gm x k p)
+ Data.Datamining.Clustering.DSOMInternal: instance Grid (gm p) => Grid (DSOM gm x k p)
+ Data.Datamining.Clustering.DSOMInternal: instance NFData (gm p) => NFData (DSOM gm x k p)
+ Data.Datamining.Clustering.DSOMInternal: instance Selector S1_0_0DSOM
+ Data.Datamining.Clustering.DSOMInternal: instance Selector S1_0_1DSOM
+ Data.Datamining.Clustering.DSOMInternal: instance Selector S1_0_2DSOM
+ Data.Datamining.Clustering.DSOMInternal: instance Selector S1_0_3DSOM
+ Data.Datamining.Clustering.SOM: [SOM] :: gm p -> (t -> d -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SOM t d gm x k p
+ Data.Datamining.Clustering.SOMInternal: [SOM] :: gm p -> (t -> d -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SOM t d gm x k p
+ Data.Datamining.Clustering.SOMInternal: instance (Foldable gm, GridMap gm p, Grid (BaseGrid gm p)) => GridMap (SOM t d gm x k) p
+ Data.Datamining.Clustering.SOMInternal: instance (GridMap gm p, k ~ Index (BaseGrid gm p), Grid (gm p), GridMap gm x, k ~ Index (gm p), k ~ Index (BaseGrid gm x), Num t, Ord x, Num x, Num d) => Classifier (SOM t d gm) x k p
+ Data.Datamining.Clustering.SOMInternal: instance (NFData t, NFData (gm p)) => NFData (SOM t d gm x k p)
+ Data.Datamining.Clustering.SOMInternal: instance Constructor C1_0SOM
+ Data.Datamining.Clustering.SOMInternal: instance Datatype D1SOM
+ Data.Datamining.Clustering.SOMInternal: instance Foldable gm => Foldable (SOM t d gm x k)
+ Data.Datamining.Clustering.SOMInternal: instance Generic (SOM t d gm x k p)
+ Data.Datamining.Clustering.SOMInternal: instance Grid (gm p) => Grid (SOM t d gm x 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: instance Selector S1_0_3SOM
+ Data.Datamining.Clustering.SOMInternal: instance Selector S1_0_4SOM
+ Data.Datamining.Clustering.SOS: [SOS] :: Map k (p, t) -> (t -> x) -> Int -> x -> (p -> p -> x) -> (p -> x -> p -> p) -> k -> SOS t x k p
+ Data.Datamining.Clustering.SOS: [diffThreshold] :: SOS t x k p -> x
+ Data.Datamining.Clustering.SOS: [difference] :: SOS t x k p -> p -> p -> x
+ Data.Datamining.Clustering.SOS: [learningRate] :: SOS t x k p -> t -> x
+ Data.Datamining.Clustering.SOS: [makeSimilar] :: SOS t x k p -> p -> x -> p -> p
+ Data.Datamining.Clustering.SOS: [maxSize] :: SOS t x k p -> Int
+ Data.Datamining.Clustering.SOS: [nextIndex] :: SOS t x k p -> k
+ Data.Datamining.Clustering.SOS: [toMap] :: SOS t x k p -> Map k (p, t)
+ Data.Datamining.Clustering.SOS: classify :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k) => SOS t x k p -> p -> (k, x, [(k, x)], SOS t x k p)
+ Data.Datamining.Clustering.SOS: counterMap :: SOS t x k p -> Map k t
+ Data.Datamining.Clustering.SOS: data SOS t x k p
+ Data.Datamining.Clustering.SOS: exponential :: (Floating a, Integral t) => a -> a -> t -> a
+ Data.Datamining.Clustering.SOS: isEmpty :: SOS t x k p -> Bool
+ Data.Datamining.Clustering.SOS: makeSOS :: Bounded k => (t -> x) -> Int -> x -> (p -> p -> x) -> (p -> x -> p -> p) -> SOS t x k p
+ Data.Datamining.Clustering.SOS: modelMap :: SOS t x k p -> Map k p
+ Data.Datamining.Clustering.SOS: numModels :: SOS t x k p -> Int
+ Data.Datamining.Clustering.SOS: time :: Num t => SOS t x k p -> t
+ Data.Datamining.Clustering.SOS: train :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k) => SOS t x k p -> p -> SOS t x k p
+ Data.Datamining.Clustering.SOS: trainBatch :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k) => SOS t x k p -> [p] -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: [SOS] :: Map k (p, t) -> (t -> x) -> Int -> x -> (p -> p -> x) -> (p -> x -> p -> p) -> k -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: [diffThreshold] :: SOS t x k p -> x
+ Data.Datamining.Clustering.SOSInternal: [difference] :: SOS t x k p -> p -> p -> x
+ Data.Datamining.Clustering.SOSInternal: [learningRate] :: SOS t x k p -> t -> x
+ Data.Datamining.Clustering.SOSInternal: [makeSimilar] :: SOS t x k p -> p -> x -> p -> p
+ Data.Datamining.Clustering.SOSInternal: [maxSize] :: SOS t x k p -> Int
+ Data.Datamining.Clustering.SOSInternal: [nextIndex] :: SOS t x k p -> k
+ Data.Datamining.Clustering.SOSInternal: [toMap] :: SOS t x k p -> Map k (p, t)
+ Data.Datamining.Clustering.SOSInternal: addModel :: (Num t, Ord t, Enum k, Ord k) => p -> SOS t x k p -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: addNode :: (Num t, Enum k, Ord k) => p -> SOS t x k p -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: classify :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k) => SOS t x k p -> p -> (k, x, [(k, x)], SOS t x k p)
+ Data.Datamining.Clustering.SOSInternal: counterMap :: SOS t x k p -> Map k t
+ Data.Datamining.Clustering.SOSInternal: counters :: SOS t x k p -> [t]
+ Data.Datamining.Clustering.SOSInternal: data SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: deleteLeastUsefulNode :: (Ord t, Ord k) => SOS t x k p -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: deleteNode :: Ord k => k -> SOS t x k p -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: exponential :: (Floating a, Integral t) => a -> a -> t -> a
+ Data.Datamining.Clustering.SOSInternal: incrementCounter :: (Num t, Ord k) => k -> SOS t x k p -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: instance (NFData t, NFData x, NFData k, NFData p) => NFData (SOS t x k p)
+ Data.Datamining.Clustering.SOSInternal: instance Constructor C1_0SOS
+ Data.Datamining.Clustering.SOSInternal: instance Datatype D1SOS
+ Data.Datamining.Clustering.SOSInternal: instance Generic (SOS t x k p)
+ Data.Datamining.Clustering.SOSInternal: instance Selector S1_0_0SOS
+ Data.Datamining.Clustering.SOSInternal: instance Selector S1_0_1SOS
+ Data.Datamining.Clustering.SOSInternal: instance Selector S1_0_2SOS
+ Data.Datamining.Clustering.SOSInternal: instance Selector S1_0_3SOS
+ Data.Datamining.Clustering.SOSInternal: instance Selector S1_0_4SOS
+ Data.Datamining.Clustering.SOSInternal: instance Selector S1_0_5SOS
+ Data.Datamining.Clustering.SOSInternal: instance Selector S1_0_6SOS
+ Data.Datamining.Clustering.SOSInternal: isEmpty :: SOS t x k p -> Bool
+ Data.Datamining.Clustering.SOSInternal: leastUsefulNode :: Ord t => SOS t x k p -> k
+ Data.Datamining.Clustering.SOSInternal: makeSOS :: Bounded k => (t -> x) -> Int -> x -> (p -> p -> x) -> (p -> x -> p -> p) -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: modelMap :: SOS t x k p -> Map k p
+ Data.Datamining.Clustering.SOSInternal: models :: SOS t x k p -> [p]
+ Data.Datamining.Clustering.SOSInternal: numModels :: SOS t x k p -> Int
+ Data.Datamining.Clustering.SOSInternal: time :: Num t => SOS t x k p -> t
+ Data.Datamining.Clustering.SOSInternal: train :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k) => SOS t x k p -> p -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: trainBatch :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k) => SOS t x k p -> [p] -> SOS t x k p
+ Data.Datamining.Clustering.SOSInternal: trainNode :: (Num t, Ord k) => SOS t x k p -> k -> p -> SOS t x k p
+ Data.Datamining.Clustering.SSOM: [SSOM] :: Map k p -> (t -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SSOM t x k p
+ Data.Datamining.Clustering.SSOMInternal: [SSOM] :: Map k p -> (t -> x) -> (p -> p -> x) -> (p -> x -> p -> p) -> t -> SSOM t x k p
+ Data.Datamining.Clustering.SSOMInternal: instance (NFData t, NFData k, NFData p) => NFData (SSOM t x k p)
+ Data.Datamining.Clustering.SSOMInternal: instance (Num t, Ord x, Num x, Ord k) => Classifier (SSOM t) x k p
+ Data.Datamining.Clustering.SSOMInternal: instance Constructor C1_0SSOM
+ Data.Datamining.Clustering.SSOMInternal: instance Datatype D1SSOM
+ Data.Datamining.Clustering.SSOMInternal: instance Generic (SSOM t x k p)
+ Data.Datamining.Clustering.SSOMInternal: instance Selector S1_0_0SSOM
+ Data.Datamining.Clustering.SSOMInternal: instance Selector S1_0_1SSOM
+ Data.Datamining.Clustering.SSOMInternal: instance Selector S1_0_2SSOM
+ Data.Datamining.Clustering.SSOMInternal: instance Selector S1_0_3SSOM
+ Data.Datamining.Clustering.SSOMInternal: instance Selector S1_0_4SSOM
+ Data.Datamining.Pattern: instance Show a => Show (NormalisedVector a)
+ Data.Datamining.Pattern: instance Show a => Show (ScaledVector a)

Files

som.cabal view
@@ -1,5 +1,5 @@ Name:              som-Version:           8.0.6+Version:           8.1.1 Stability:         experimental Synopsis:          Self-Organising Maps. Description:       A Kohonen Self-organising Map (SOM) maps input patterns @@ -33,7 +33,7 @@ source-repository this   type:     git   location: https://github.com/mhwombat/som.git-  tag:      8.0.6+  tag:      8.1.1   library@@ -51,6 +51,8 @@                    Data.Datamining.Clustering.DSOMInternal,                    Data.Datamining.Clustering.SSOM,                    Data.Datamining.Clustering.SSOMInternal,+                   Data.Datamining.Clustering.SOS,+                   Data.Datamining.Clustering.SOSInternal,                    Data.Datamining.Clustering.Classifier,                    Data.Datamining.Pattern @@ -72,5 +74,6 @@   other-modules:   Data.Datamining.Clustering.SOMQC,                    Data.Datamining.Clustering.DSOMQC,                    Data.Datamining.Clustering.SSOMQC,+                   Data.Datamining.Clustering.SOSQC,                    Data.Datamining.PatternQC 
+ src/Data/Datamining/Clustering/SOS.hs view
@@ -0,0 +1,59 @@+------------------------------------------------------------------------+-- |+-- Module      :  Data.Datamining.Clustering.SOS+-- Copyright   :  (c) Amy de Buitléir 2012-2015+-- License     :  BSD-style+-- Maintainer  :  amy@nualeargais.ie+-- Stability   :  experimental+-- Portability :  portable+--+-- A Self-organising Set (SOS). An SOS maps input patterns+-- onto a set, where each element in the set is a model of the input+-- data. An SOS is like a Kohonen Self-organising Map (SOM), except:+--+-- * Instead of a grid, it uses a simple set of unconnected models.+--   Since the models are unconnected, only the model that best matches+--   the input is ever updated. This makes it faster, however,+--   topological relationships within the input data are not preserved.+-- * New models are created on-the-fly when no existing model is+--   similar enough to an input pattern. If the SOS is at capacity,+--   the least useful model will be deleted.+--+-- This implementation supports the use of non-numeric patterns.+--+-- In layman's terms, a SOS can be useful when you you want to build+-- a set of models on some data. A tutorial is available at+-- <https://github.com/mhwombat/som/wiki>.+--+-- References:+--+-- * de Buitléir, Amy, Russell, Michael and Daly, Mark. (2012). Wains:+--   A pattern-seeking artificial life species. Artificial Life, 18 (4),+--   399-423. +-- +-- * Kohonen, T. (1982). Self-organized formation of topologically +--   correct feature maps. Biological Cybernetics, 43 (1), 59–69.+------------------------------------------------------------------------++module Data.Datamining.Clustering.SOS+  (+    -- * Construction+    SOS(..),+    makeSOS,+    -- * Deconstruction+    time,+    isEmpty,+    numModels,+    modelMap,+    counterMap,+    -- models,+    -- counters,+    -- * Learning and classification+    exponential,+    classify,+    train,+    trainBatch+  ) where++import Data.Datamining.Clustering.SOSInternal+
+ src/Data/Datamining/Clustering/SOSInternal.hs view
@@ -0,0 +1,231 @@+------------------------------------------------------------------------+-- |+-- Module      :  Data.Datamining.Clustering.SOSInternal+-- Copyright   :  (c) Amy de Buitléir 2012-2015+-- License     :  BSD-style+-- Maintainer  :  amy@nualeargais.ie+-- Stability   :  experimental+-- Portability :  portable+--+-- A module containing private @SOS@ internals. Most developers should+-- use @SOS@ instead. This module is subject to change without notice.+--+------------------------------------------------------------------------+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances,+    MultiParamTypeClasses, DeriveAnyClass, DeriveGeneric #-}++module Data.Datamining.Clustering.SOSInternal where++import Prelude hiding (lookup)++import Control.DeepSeq (NFData)+import Data.List (minimumBy, foldl')+import Data.Ord (comparing)+import qualified Data.Map.Strict as M+import GHC.Generics (Generic)++-- | A typical learning function for classifiers.+--   @'exponential' r0 d t@ returns the learning rate at time @t@.+--   When @t = 0@, the learning rate is @r0@.+--   Over time the learning rate decays exponentially; the decay rate is+--   @d@.+--   Normally the parameters are chosen such that:+--+--   * 0 < r0 < 1+--+--   * 0 < d+exponential :: (Floating a, Integral t) => a -> a -> t -> a+exponential r0 d t = r0 * exp (-d*t')+  where t' = fromIntegral t++-- | A Simplified Self-Organising Map (SOS).+--   @t@ is the type of the counter.+--   @x@ is the type of the learning rate and the difference metric.+--   @k@ is the type of the model indices.+--   @p@ is the type of the input patterns and models.+data SOS t x k p = SOS+  {+    -- | Maps patterns and match counts to nodes.+    toMap :: M.Map k (p, t),+    -- | A function which determines the learning rate for a node.+    --   The input parameter indicates how many patterns (or pattern+    --   batches) have previously been presented to the classifier.+    --   Typically this is used to make the learning rate decay over+    --   time.+    --   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.+    learningRate :: t -> x,+    -- | The maximum number of models this SOS can hold.+    maxSize :: Int,+    -- | The threshold that triggers creation of a new model.+    diffThreshold :: x,+    -- | A function which 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 -> x,+    -- | A function which updates models.+    --   For example, if this function is @f@, then+    --   @f 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@+    --   parameter, which should be a number between 0 and 1.+    --   Larger 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 -> x -> p -> p,+    -- | Index for the next node to add to the SOS.+    nextIndex :: k+  } deriving (Generic, NFData)++-- @'makeSOS' lr n dt diff ms@ creates a new SOS that does not (yet)+-- contain any models.+-- It will learn at the rate determined by the learning function @lr@,+-- and will be able to hold up to @n@ models.+-- It will create a new model based on a pattern presented to it when+-- (1) the SOS contains no models, or+-- (2) the difference between the pattern and the closest matching+-- model exceeds the threshold @dt@.+-- It will use the function @diff@ to measure the similarity between+-- an input pattern and a model.+-- It will use the function @ms@ to adjust models as needed to make+-- them more similar to input patterns.+makeSOS+  :: Bounded k+    => (t -> x) -> Int -> x -> (p -> p -> x) -> (p -> x -> p -> p)+      -> SOS t x k p+makeSOS lr n dt diff ms =+  if n <= 0+    then error "max size for SOS <= 0"+    else SOS M.empty lr n dt diff ms minBound++-- | Returns true if the SOS has no models, false otherwise.+isEmpty :: SOS t x k p -> Bool+isEmpty = M.null . toMap++-- | Returns the number of models the SOS currently contains.+numModels :: SOS t x k p -> Int+numModels = length . M.keys . toMap++-- | Returns a map from node ID to model.+modelMap :: SOS t x k p -> M.Map k p+modelMap = M.map fst . toMap++-- | Returns a map from node ID to counter (number of times the+--   node's model has been the closest match to an input pattern).+counterMap :: SOS t x k p -> M.Map k t+counterMap = M.map snd . toMap++-- | Returns the current models.+models :: SOS t x k p -> [p]+models = map fst . M.elems . toMap++-- | Returns the current counters (number of times the+--   node's model has been the closest match to an input pattern).+counters :: SOS t x k p -> [t]+counters = map snd . M.elems . toMap++-- | The current "time" (number of times the SOS has been trained).+time :: Num t => SOS t x k p -> t+time = sum . map snd . M.elems . toMap++-- | Adds a new node to the SOS.+addNode+  :: (Num t, Enum k, Ord k)+    => p -> SOS t x k p -> SOS t x k p+addNode p s = if numModels s >= maxSize s+                then error "SOS is full"+                else s { toMap=gm', nextIndex=succ k }+  where gm = toMap s+        k = nextIndex s+        gm' = M.insert k (p, 0) gm++-- | Removes a node from the SOS.+--   Deleted nodes are never re-used.+deleteNode :: Ord k => k -> SOS t x k p -> SOS t x k p+deleteNode k s = s { toMap=gm' }+  where gm = toMap s+        gm' = if M.member k gm+                then M.delete k gm+                else error "no such node"++incrementCounter :: (Num t, Ord k) => k -> SOS t x k p -> SOS t x k p+incrementCounter k s = s { toMap=gm' }+  where gm = toMap s+        gm' = if M.member k gm+                then M.adjust inc k gm+                else error "no such node"+        inc (p, t) = (p, t+1)++-- | Trains the specified node to better match a target.+--   Most users should use @'train'@, which automatically determines+--   the BMU and trains it.+trainNode+  :: (Num t, Ord k)+    => SOS t x k p -> k -> p -> SOS t x k p+trainNode s k target = s { toMap=gm' }+  where gm = toMap s+        gm' = M.adjust tweakModel k gm+        r = (learningRate s) (time s)+        tweakModel (p, t) = (makeSimilar s target r p, t)++leastUsefulNode :: Ord t => SOS t x k p -> k+leastUsefulNode s = if isEmpty s+                      then error "SOS has no nodes"+                      else fst . minimumBy (comparing (snd . snd))+                             . M.toList . toMap $ s++deleteLeastUsefulNode :: (Ord t, Ord k) => SOS t x k p -> SOS t x k p+deleteLeastUsefulNode s = deleteNode k s+  where k = leastUsefulNode s++addModel+  :: (Num t, Ord t, Enum k, Ord k)+    => p -> SOS t x k p -> SOS t x k p+addModel p s = addNode p s'+  where s' = if numModels s >= maxSize s+                then deleteLeastUsefulNode s+                else s++-- reportAddModel+--   :: (Num t, Ord t, Num x, Enum k, Ord k)+--     => SOS t x k p -> p -> (k, x, [(k, x)], SOS t x k p)+-- reportAddModel s p = (k, 0, [(k, 0)], s'')+--   where (k, s') = addModel p s+--         s'' = incrementCounter k s'++-- | @'classify' s p@ identifies the model @s@ that most closely+--   matches the pattern @p@.+--   If necessary, it will create a new node and model.+--   Returns the ID of the node with the best matching model,+--   the difference between the best matching model and the pattern,+--   the differences between the input and each model in the SOS,+--   and the (possibly updated) SOS.+classify+  :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k)+    => SOS t x k p -> p -> (k, x, [(k, x)], SOS t x k p)+classify s p+  | isEmpty s                 = classify (addModel p s) p+  | bmuDiff > diffThreshold s = classify (addModel p s) p+  | otherwise                 = (bmu, bmuDiff, diffs, s')+  where (bmu, bmuDiff) = minimumBy (comparing snd) diffs+        diffs = M.toList . M.map (difference s p) . M.map fst+                    . toMap $ s+        s' = incrementCounter bmu s++-- | @'train' s p@ identifies the model in @s@ that most closely+--   matches @p@, and updates it to be a somewhat better match.+train+  :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k)+    => SOS t x k p -> p -> SOS t x k p+train s p = trainNode s' bmu p+  where (bmu, _, _, s') = classify s p++-- | For each pattern @p@ in @ps@, @'trainBatch' s ps@ identifies the+--   model in @s@ that most closely matches @p@,+--   and updates it to be a somewhat better match.+trainBatch+  :: (Num t, Ord t, Num x, Ord x, Enum k, Ord k)+    => SOS t x k p -> [p] -> SOS t x k p+trainBatch = foldl' train
+ test/Data/Datamining/Clustering/SOSQC.hs view
@@ -0,0 +1,211 @@+------------------------------------------------------------------------+-- |+-- Module      :  Data.Datamining.Clustering.SOSQC+-- Copyright   :  (c) Amy de Buitléir 2012-2015+-- License     :  BSD-style+-- Maintainer  :  amy@nualeargais.ie+-- Stability   :  experimental+-- Portability :  portable+--+-- Tests+--+------------------------------------------------------------------------+{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleInstances,+    FlexibleContexts #-}+{-# OPTIONS_GHC -fno-warn-type-defaults -fno-warn-orphans #-}++module Data.Datamining.Clustering.SOSQC+  (+    test+  ) where++import Data.Datamining.Pattern (adjustNum, absDifference)+import Data.Datamining.Clustering.SOSInternal+import Data.List (minimumBy)+import qualified Data.Map.Strict as M+import Data.Ord (comparing)+import Data.Word (Word16)+import System.Random (Random)+import Test.Framework as TF (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck ((==>), Gen, Arbitrary, Property, Positive,+  arbitrary, shrink, choose, property, sized, suchThat, vectorOf,+  getPositive)++newtype UnitInterval a = UnitInterval {getUnitInterval :: a}+ deriving ( Eq, Ord, Show, Read)++instance Functor UnitInterval where+  fmap f (UnitInterval x) = UnitInterval (f x)++instance (Num a, Ord a, Random a, Arbitrary a)+    => Arbitrary (UnitInterval a) where+  arbitrary = fmap UnitInterval $ choose (0,1)+  shrink (UnitInterval x) =+    [ UnitInterval x' | x' <- shrink x, x' >= 0, x' <= 1]++prop_Exponential_starts_at_r0+  :: UnitInterval Double -> Positive Double -> Property+prop_Exponential_starts_at_r0 r0 d+  = property $ abs (exponential r0' d' 0 - r0') < 0.01+  where r0' = getUnitInterval r0+        d' = getPositive d++prop_Exponential_ge_0+  :: UnitInterval Double -> Positive Double -> Positive Int -> Property+prop_Exponential_ge_0 r0 d t = property $ exponential r0' d' t' >= 0+  where r0' = getUnitInterval r0+        d' = getPositive d+        t' = getPositive t++positive :: (Num a, Ord a, Arbitrary a) => Gen a+positive = arbitrary `suchThat` (> 0)++data TestSOS = TestSOS (SOS Int Double Word16 Double) String++instance Show TestSOS where+  show (TestSOS _ desc) = desc++buildTestSOS+  :: Double -> Double -> Int -> Double -> [Double] -> TestSOS+buildTestSOS r0 d maxSz dt ps = TestSOS s' desc+  where lrf = exponential r0 d+        s = makeSOS lrf maxSz dt absDifference adjustNum+        desc = "buildTestSOS " ++ show r0 ++ " " ++ show d+                 ++ " " ++ show maxSz+                 ++ " " ++ show dt+                 ++ " " ++ show ps+        s' = trainBatch s ps++sizedTestSOS :: Int -> Gen TestSOS+sizedTestSOS n = do+  maxSz <- choose (1, n+1)+  let numPatterns = n+  r0 <- choose (0, 1)+  d <- positive+  dt <- choose (0, 1)+  ps <- vectorOf numPatterns arbitrary+  return $ buildTestSOS r0 d maxSz dt ps++instance Arbitrary TestSOS where+  arbitrary = sized sizedTestSOS++prop_classify_increments_counter :: TestSOS -> Double -> Property+prop_classify_increments_counter (TestSOS s _) x+  = numModels s < maxSize s ==> countAfter == countBefore + 1+  -- We have to check if the SOS is full, otherwise we'll replace an+  -- existing model (and its counter), which means that the total+  -- count could change by an arbitrary amount.+  where countBefore = time s+        countAfter = time s'+        (_, _, _, s') = classify s x++prop_classify_chooses_best_fit :: TestSOS -> Double -> Property+prop_classify_chooses_best_fit (TestSOS s _) x+  = property $ bmu == fst (minimumBy (comparing snd) diffs)+  where (bmu, _, diffs, _) = classify s x++prop_trainNode_reduces_diff :: TestSOS -> Double -> Property+prop_trainNode_reduces_diff (TestSOS s _) x = not (isEmpty s) ==>+  diffAfter < diffBefore || diffBefore == 0+                         || learningRate s (time s) < 1e-10+  where (bmu, diffBefore, _, s2) = classify s x+        s3 = trainNode s2 bmu x+        (_, diffAfter, _, _) = classify s3 x++prop_diff_lt_threshold_after_training :: TestSOS -> Double -> Property+prop_diff_lt_threshold_after_training (TestSOS s _) x =+  property $ diffAfter < diffThreshold s+  where s' = train s x+        (_, diffAfter, _, _) = classify s' x++prop_training_reduces_diff :: TestSOS -> Double -> Property+prop_training_reduces_diff (TestSOS s _) x = not (isEmpty s) ==>+  diffAfter < diffBefore || diffBefore == 0+                         || learningRate s (time s) < 1e-10+  where (_, diffBefore, _, s2) = classify s x+        s3 = train s2 x+        (_, diffAfter, _, _) = classify s3 x++-- TODO prop: map will never exceed maxSize++prop_train_only_modifies_one_model+  :: TestSOS -> Double -> Property+prop_train_only_modifies_one_model (TestSOS s _) p+  = numModels s < maxSize s ==> otherModelsBefore == otherModelsAfter+    where (bmu, _, _, s2) = classify s p+          s3 = train s2 p+          otherModelsBefore = M.delete bmu . M.map fst . toMap $ s2+          otherModelsAfter = M.delete bmu . M.map fst . toMap $ s3++prop_train_increments_counter :: TestSOS -> Double -> Property+prop_train_increments_counter (TestSOS s _) x+  = numModels s < maxSize s ==> countAfter == countBefore + 1+  -- We have to check if the SOS is full, otherwise we'll replace an+  -- existing model (and its counter), which means that the total+  -- count could change by an arbitrary amount.+  where countBefore = time s+        countAfter = time $ train s x++-- | The training set consists of the same vectors in the same order,+--   several times over. So the resulting classifications should consist+--   of the same integers in the same order, over and over.+prop_batch_training_works :: TestSOS -> [Double] -> Property+prop_batch_training_works (TestSOS s _) ps+  -- = maxSize s > length ps+  --   ==> classifications == (concat . replicate 5) firstSet+  = property $ classifications == (concat . replicate 5) firstSet+  where trainingSet = (concat . replicate 5) ps+        sRightSize = if maxSize s >= length ps+          then s+          else s { maxSize=length ps + 1}+        s' = trainBatch sRightSize trainingSet+        classifications = map (justBMU . classify s') trainingSet+        justBMU = \(bmu, _, _, _) -> bmu+        firstSet = take (length ps) classifications++-- | WARNING: This can fail when two nodes are close enough in+--   value so that after training they become identical.+prop_classification_is_consistent :: TestSOS -> Double -> Property+prop_classification_is_consistent (TestSOS s _) x+  = property $ bmu == bmu'+  where (bmu, _, _, s2) = classify s x+        s3 = train s2 x+        (bmu', _, _, _) = classify s3 x++prop_classification_stabilises+  :: TestSOS -> [Double] -> Property+prop_classification_stabilises (TestSOS s _)  ps+  = (not . null $ ps) && maxSize s > length ps ==> k2 == k1+  where sStable = trainBatch s . concat . replicate 10 $ ps+        (k1, _, _, sStable2) = classify sStable (head ps)+        sStable3 = trainBatch sStable2 ps+        (k2, _, _, _) = classify sStable3 (head ps)++test :: Test+test = testGroup "QuickCheck Data.Datamining.Clustering.SOS"+  [+    testProperty "prop_Exponential_starts_at_r0"+      prop_Exponential_starts_at_r0,+    testProperty "prop_Exponential_ge_0"+      prop_Exponential_ge_0,+    testProperty "prop_classify_increments_counter"+      prop_classify_increments_counter,+    testProperty "prop_classify_chooses_best_fit"+      prop_classify_chooses_best_fit,+    testProperty "prop_trainNode_reduces_diff"+      prop_trainNode_reduces_diff,+    testProperty "prop_diff_lt_threshold_after_training"+      prop_diff_lt_threshold_after_training,+    testProperty "prop_training_reduces_diff"+      prop_training_reduces_diff,+    testProperty "prop_train_only_modifies_one_model"+      prop_train_only_modifies_one_model,+    testProperty "prop_train_increments_counter"+      prop_train_increments_counter,+    testProperty "prop_batch_training_works" prop_batch_training_works,+    testProperty "prop_classification_is_consistent"+      prop_classification_is_consistent,+    testProperty "prop_classification_stabilises"+      prop_classification_stabilises+  ]