SimpleEA 0.1.1 → 0.2
raw patch · 3 files changed
+45/−34 lines, 3 filesdep +mersenne-random-pure64PVP ok
version bump matches the API change (PVP)
Dependencies added: mersenne-random-pure64
API changes (from Hackage documentation)
- AI.SimpleEA: runEA :: [Genome a] -> FitnessFunc a -> SelectionFunction a -> RecombinationOp a -> MutationOp a -> StdGen -> [[(Genome a, Fitness)]]
+ AI.SimpleEA: runEA :: [Genome a] -> FitnessFunc a -> SelectionFunction a -> RecombinationOp a -> MutationOp a -> PureMT -> [[(Genome a, Fitness)]]
- AI.SimpleEA: type MutationOp a = Genome a -> Rand StdGen (Genome a)
+ AI.SimpleEA: type MutationOp a = Genome a -> Rand PureMT (Genome a)
- AI.SimpleEA: type RecombinationOp a = (Genome a, Genome a) -> Rand StdGen (Genome a, Genome a)
+ AI.SimpleEA: type RecombinationOp a = (Genome a, Genome a) -> Rand PureMT (Genome a, Genome a)
- AI.SimpleEA: type SelectionFunction a = [(Genome a, Fitness)] -> Rand StdGen [Genome a]
+ AI.SimpleEA: type SelectionFunction a = [(Genome a, Fitness)] -> Rand PureMT [Genome a]
- AI.SimpleEA.Utils: fitPropSelect :: [(a, Fitness)] -> Rand StdGen a
+ AI.SimpleEA.Utils: fitPropSelect :: RandomGen g => [(a, Fitness)] -> Rand g a
- AI.SimpleEA.Utils: randomGenomes :: (Random a, Enum a) => Int -> a -> a -> StdGen -> [Genome a]
+ AI.SimpleEA.Utils: randomGenomes :: (RandomGen g, Random a, Enum a) => Int -> Int -> a -> a -> Rand g [Genome a]
- AI.SimpleEA.Utils: tournamentSelect :: [(a, Fitness)] -> Int -> Rand StdGen a
+ AI.SimpleEA.Utils: tournamentSelect :: [(a, Fitness)] -> Int -> Rand PureMT a
Files
- AI/SimpleEA.hs +27/−19
- AI/SimpleEA/Utils.hs +10/−9
- SimpleEA.cabal +8/−6
AI/SimpleEA.hs view
@@ -29,8 +29,12 @@ ) where import Control.Monad.Random+import System.Random.Mersenne.Pure64 +-- | An individual's fitness is simply a number. type Fitness = Double++-- | A genome is a list (e.g. a 'String'). type Genome a = [a] -- | A fitness functions assigns a fitness score to a genome. The rest of the@@ -41,14 +45,15 @@ -- | A selection function is responsible for selection. It takes pairs of -- genomes and their fitness and is responsible for returning one or more -- individuals.-type SelectionFunction a = [(Genome a, Fitness)] -> Rand StdGen [Genome a]+type SelectionFunction a = [(Genome a, Fitness)] -> Rand PureMT [Genome a] -- | A recombination operator takes two /parent/ genomes and returns two -- /children/.-type RecombinationOp a = (Genome a, Genome a) -> Rand StdGen (Genome a, Genome a)+type RecombinationOp a = (Genome a, Genome a) -> Rand PureMT (Genome a, Genome a) --- | A mutation operator takes a genome and returns an altered copy of it.-type MutationOp a = Genome a -> Rand StdGen (Genome a)+-- | A mutation operator takes a genome and returns (a possibly altered) copy+-- of it.+type MutationOp a = Genome a -> Rand PureMT (Genome a) -- | Runs the evolutionary algorithm with the given start population. This will -- produce an infinite list of generations and 'take' or 'takeWhile' should be@@ -70,7 +75,7 @@ SelectionFunction a -> RecombinationOp a -> MutationOp a ->- StdGen ->+ PureMT -> [[(Genome a,Fitness)]] runEA startPop fitFun selFun recOp mutOp g = let p = zip startPop (map (`fitFun` startPop) startPop)@@ -82,7 +87,7 @@ FitnessFunc a -> RecombinationOp a -> MutationOp a ->- Rand StdGen [[(Genome a, Fitness)]]+ Rand PureMT [[(Genome a, Fitness)]] generations !pop selFun fitFun recOp mutOp = do -- first, select parents for the new generation newGen <- selFun pop@@ -98,7 +103,7 @@ return $ pop : nextGens -doRecombinations :: [Genome a] -> RecombinationOp a -> Rand StdGen [Genome a]+doRecombinations :: [Genome a] -> RecombinationOp a -> Rand PureMT [Genome a] doRecombinations [] _ = return [] doRecombinations [_] _ = error "odd number of parents" doRecombinations (a:b:r) rec = do@@ -115,21 +120,24 @@ >import AI.SimpleEA >import AI.SimpleEA.Utils >+>import System.Random.Mersenne.Pure64 >import Control.Monad.Random >import Data.List >import System.Environment (getArgs) >import Control.Monad (unless) -The @numOnes@ function will function as our 'FitnessFunc' and simply returns the number of @1@'s-in the string.+The @numOnes@ function will function as our 'FitnessFunc' and simply returns+the number of @1@'s in the string. It ignores the rest of the population (the+second parameter) since the fitness is not relative to the other individuals in+the generation. >numOnes :: FitnessFunc Char >numOnes g _ = (fromIntegral . length . filter (=='1')) g The @select@ function is our 'SelectionFunction'. It uses sigma-scaled, fitness-proportionate selection. 'sigmaScale' is defined in 'SimpleEA.Utils'. By first taking the four-best genomes (by using the @elite@ function) we get elitism, making sure that-maximum fitness never decreases.+best genomes (by using the @elite@ function) we make sure that maximum fitness+never decreases ('elitism'). >select :: SelectionFunction Char >select gs = select' (take 4 $ elite gs)@@ -143,9 +151,9 @@ > let newPop = p1:p2:gs' > select' newPop -Crossover consists of finding a crossover point along the length of the genomes-and swapping what comes after between the two genomes. The parameter @p@-determines the likelihood of crossover taking place.+Crossover is done by finding a crossover point along the length of the genomes+and swapping what comes after that point between the two genomes. The parameter+@p@ determines the likelihood of crossover taking place. >crossOver :: Double -> RecombinationOp Char >crossOver p (g1,g2) = do@@ -156,7 +164,8 @@ > return (take r g1 ++ drop r g2, take r g2 ++ drop r g1) > else return (g1,g2) -Mutation flips a random bit along the length of the genome with probability @p@.+The mutation operator @mutate@ flips a random bit along the length of the+genome with probability @p@. >mutate :: Double -> MutationOp Char >mutate p g = do@@ -179,10 +188,9 @@ >main = do > args <- getArgs-> g <- newStdGen-> let (g1,g2) = split g-> let p = take 100 $ randomGenomes 20 '0' '1' g1-> let gs = take 101 $ runEA p numOnes select (crossOver 0.75) (mutate 0.01) g2+> g <- newPureMT+> let (p,g') = runRand (randomGenomes 100 20 '0' '1') g+> let gs = take 101 $ runEA p numOnes select (crossOver 0.75) (mutate 0.01) g' > let fs = avgFitnesses gs > let ms = maxFitnesses gs > let ds = stdDeviations gs
AI/SimpleEA/Utils.hs view
@@ -19,9 +19,10 @@ , getPlottingData ) where -import Control.Monad (liftM)+import Control.Monad (liftM, replicateM) import Control.Monad.Random import Data.List (genericLength, zip4, sortBy, nub, elemIndices, sort)+import System.Random.Mersenne.Pure64 (PureMT) import AI.SimpleEA -- |Returns the average fitnesses for a list of generations.@@ -48,12 +49,12 @@ mean = sum p/len sqDiffs = map (\n -> (n-mean)**2) p --- |Returns an infinite list of random genomes of length @len@ made of elements--- in the range @[from,to]@-randomGenomes :: (Random a, Enum a) => Int -> a -> a -> StdGen -> [Genome a]-randomGenomes len from to = do- l <- randomRs (from,to)- return $ nLists len l+-- |Returns a list of @len@ random genomes who has length @genomeLen@ made of+-- elements in the range @[from,to]@.+randomGenomes :: (RandomGen g, Random a, Enum a) => Int -> Int -> a -> a -> Rand g [Genome a]+randomGenomes len genomeLen from to = do+ l <- replicateM (len*genomeLen) $ getRandomR (from,to)+ return $ nLists genomeLen l where nLists :: Int -> [a] -> [[a]] nLists _ [] = [] nLists n ls = take n ls : nLists n (drop n ls)@@ -76,7 +77,7 @@ -- |Fitness-proportionate selection: select a random item from a list of (item, -- score) where each item's chance of being selected is proportional to its -- score-fitPropSelect :: [(a, Fitness)] -> Rand StdGen a+fitPropSelect :: (RandomGen g) => [(a, Fitness)] -> Rand g a fitPropSelect xs = do let xs' = zip (map fst xs) (scanl1 (+) $ map snd xs) let sumScores = (snd . last) xs'@@ -84,7 +85,7 @@ return $ (fst . head . dropWhile ((rand >) . snd)) xs' -- |Performs tournament selection amoing @size@ individuals and returns the winner-tournamentSelect :: [(a, Fitness)] -> Int -> Rand StdGen a+tournamentSelect :: [(a, Fitness)] -> Int -> Rand PureMT a tournamentSelect xs size = do let l = length xs rs <- liftM (take size . nub) $ getRandomRs (0,l-1)
SimpleEA.cabal view
@@ -1,7 +1,7 @@ name: SimpleEA-category: AI+category: AI build-type: Simple-version: 0.1.1+version: 0.2 synopsis: Simple evolutionary algorithm framework. description: Simple framework for running an evolutionary algorithm by providing selection, recombination, and mutation operators.@@ -11,15 +11,17 @@ author: Erlend Hamberg maintainer: ehamberg@gmail.com stability: experimental-tested-with: GHC==7.0.1+tested-with: GHC==7.4.1 homepage: http://www.github.com/ehamberg/simpleea/ cabal-version: >=1.6 Library- build-depends: base >=4 && < 5, MonadRandom- ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-orphans- exposed-modules: AI.SimpleEA, AI.SimpleEA.Utils+ build-depends: base >=4 && < 5,+ MonadRandom,+ mersenne-random-pure64 >= 0.2 && < 0.3+ ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-orphans+ exposed-modules: AI.SimpleEA, AI.SimpleEA.Utils source-repository head type: git