@@ -1,14 +1,21 @@
1+{-# LANGUAGE TupleSections #-}
1module KMeans
2module KMeans
4 , localKMeans
5 , localKMeans
7import Data.List (minimumBy)
12import Data.List (minimumBy)
8import Data.Function (on)
13import Data.Function (on)
9import Data.Array (Array, (!), bounds)
14import Data.Array (Array, (!), bounds)
10−import qualified Data.Map as Map (fromList)
15+import qualified Data.Map as Map (fromList, elems, toList, size)
11−import MapReduce
16+import Control.Distributed.Process
17+import Control.Distributed.Process.Closure
18+import MapReduce hiding (__remoteTable)
13type Point = (Double, Double)
20type Point = (Double, Double)
14type Cluster = (Double, Double)
21type Cluster = (Double, Double)
@@ -36,6 +43,84 @@ kmeans points = MapReduce {
36 , mrReduce = \_ ps -> (ps, center ps)
43 , mrReduce = \_ ps -> (ps, center ps)
39−localKMeans :: Array Int Point -> [Cluster] -> Map Cluster ([Point], Point)
46+localKMeans :: Array Int Point
49+ -> Map Cluster ([Point], Point)
40−localKMeans points cs =
50+localKMeans points cs iterations = go (iterations - 1)
52+ mr :: [Cluster] -> Map Cluster ([Point], Point)
41− localMapReduce (kmeans points) (Map.fromList [(bounds points, cs)])
53+ mr = localMapReduce (kmeans points) . trivialSegmentation
55+ go :: Int -> Map Cluster ([Point], Point)
57+ go n = mr . map snd . Map.elems . go $ n - 1
59+ trivialSegmentation :: [Cluster] -> Map (Int, Int) [Cluster]
60+ trivialSegmentation cs' = Map.fromList [(bounds points, cs')]
62+dictIn :: SerializableDict (ProcessId, (Int, Int), [Cluster])
63+dictIn = SerializableDict
65+dictOut :: SerializableDict [(Cluster, Point)]
66+dictOut = SerializableDict
68+remotable ['kmeans, 'dictIn, 'dictOut]
70+distrKMeans :: Array Int Point
74+ -> Process (Map Cluster ([Point], Point))
75+distrKMeans points cs mappers iterations =
76+ distrMapReduce $(mkStatic 'dictIn)
78+ ($(mkClosure 'kmeans) points)
80+ (go (iterations - 1))
83+ -> (Map (Int, Int) [Cluster] -> Process (Map Cluster ([Point], Point)))
84+ -> Process (Map Cluster ([Point], Point))
86+ iteration (Map.fromList $ map (, cs) segments)
88+ clusters <- go (n - 1) iteration
89+ let centers = map snd $ Map.elems clusters
90+ iteration (Map.fromList $ map (, centers) segments)
92+ segments :: [(Int, Int)]
93+ segments = let (lo, _) = bounds points in dividePoints numPoints lo
95+ dividePoints :: Int -> Int -> [(Int, Int)]
96+ dividePoints pointsLeft offset
97+ | pointsLeft <= pointsPerMapper = [(offset, offset + pointsLeft - 1)]
98+ | otherwise = let offset' = offset + pointsPerMapper in
99+ (offset, offset' - 1)
100+ : dividePoints (pointsLeft - pointsPerMapper) offset'
102+ pointsPerMapper :: Int
104+ ceiling (toRational numPoints / toRational (length mappers))
107+ numPoints = let (lo, hi) = bounds points in hi - lo + 1
109+-- | Create a gnuplot data file for the output of the k-means algorithm
111+-- To plot the data, use
113+-- > plot "<<filename>>" u 1:2:3 with points palette
114+createGnuPlot :: Map KMeans.Cluster ([KMeans.Point], KMeans.Point) -> Handle -> IO ()
115+createGnuPlot clusters h =
116+ mapM_ printPoint . flatten . zip colors . Map.toList $ clusters
118+ printPoint (x, y, color) =
119+ hPutStrLn h $ show x ++ " " ++ show y ++ " " ++ show color
121+ flatten :: [(Float, (KMeans.Cluster, ([KMeans.Point], KMeans.Point)))]
122+ -> [(Double, Double, Float)]
123+ flatten = concat . map (\(color, (_, (points, _))) -> map (\(x, y) -> (x, y, color)) points)
126+ colors = [0, 1 / fromIntegral (Map.size clusters) .. 1]