{-# LANGUAGE BangPatterns #-} {- | Common utility functions. -} module Moo.GeneticAlgorithm.Utilities ( -- * Non-deterministic functions getRandomGenomes , doCrossovers , doNCrossovers ) where import Moo.GeneticAlgorithm.Types import Moo.GeneticAlgorithm.Random import Control.Monad.Mersenne.Random import Control.Monad (replicateM) -- | Generate @n@ random genomes made of elements in the -- hyperrectangle ranges @[(from_i,to_i)]@. Return a list of genomes -- and a new state of random number generator. randomGenomes :: (Random a, Ord a) => PureMT -- ^ random number generator -> Int -- ^ n, number of genomes to generate -> [(a, a)] -- ^ ranges for individual genome elements -> ([Genome a], PureMT) randomGenomes rng n ranges = let sortRange (r1,r2) = (min r1 r2, max r1 r2) ranges' = map sortRange ranges in flip runRandom rng $ replicateM n $ mapM getRandomR ranges' -- | Generate @n@ uniform random genomes with individual genome -- elements bounded by @ranges@. This corresponds to random uniform -- sampling of points (genomes) from a hyperrectangle with a bounding -- box @ranges@. getRandomGenomes :: (Random a, Ord a) => Int -- ^ @n@, how many genomes to generate -> [(a, a)] -- ^ ranges for individual genome elements -> Rand ([Genome a]) -- ^ random genomes getRandomGenomes n ranges = Rand $ \rng -> let (gs, rng') = randomGenomes rng n ranges in R gs rng' -- | Crossover all available parents. Parents are not repeated. doCrossovers :: [Genome a] -> CrossoverOp a -> Rand [Genome a] doCrossovers [] _ = return [] doCrossovers parents xover = do (children', parents') <- xover parents if null children' then return [] else do rest <- doCrossovers parents' xover return $ children' ++ rest -- | Produce exactly @n@ offsprings by repeatedly running the @crossover@ -- operator between randomly selected parents (possibly repeated). doNCrossovers :: Int -- ^ @n@, number of offsprings to generate -> [Genome a] -- ^ @parents@' genomes -> CrossoverOp a -- ^ @crossover@ operator -> Rand [Genome a] doNCrossovers _ [] _ = return [] doNCrossovers n parents xover = doAnotherNCrossovers n [] where doAnotherNCrossovers i children | i <= 0 = return . take n . concat $ children | otherwise = do (children', _) <- xover =<< shuffle parents if (null children') then doAnotherNCrossovers 0 children -- no more children else doAnotherNCrossovers (i - length children') (children':children)