packages feed

moo-1.2: Tests/Internals/TestCrossover.hs

module Tests.Internals.TestCrossover where


import Test.HUnit
import System.Random.Mersenne.Pure64 (pureMT)
import Data.List (group, transpose)


import Moo.GeneticAlgorithm.Types
import Moo.GeneticAlgorithm.Crossover
import Moo.GeneticAlgorithm.Random



testCrossover =
    TestList
    [ "do N crossovers" ~: do
        let genomes = [[1,1,1,1],[0,0,0,0]] :: [[Int]]
        let result4 = flip evalRand (pureMT 1) $
                      doNCrossovers 4 genomes (onePointCrossover 0.5)
        let expected4 = [[1,0,0,0],[0,1,1,1],[1,1,0,0],[0,0,1,1]]
        assertEqual "4 crossovers" expected4 result4
        let genesums4 = map sum . transpose $ result4
        assertEqual "gene-sums (4 genomes)" [2,2,2,2] genesums4
        let result3 = flip evalRand (pureMT 1) $
                      doNCrossovers 3 genomes (onePointCrossover 0.5)
        let expected3 = [[1,0,0,0],[0,1,1,1],[1,1,0,0]]
        assertEqual "3 crossovers" expected3 result3
    , "do all crossovers" ~: do
        let genomes = [[1,1,1,1],[0,0,0,0]] :: [[Int]]
        let result = flip evalRand (pureMT 1) $
                     doCrossovers genomes (onePointCrossover 0.5)
        let expected = [[1,1,0,0],[0,0,1,1]]
        assertEqual "all crossovers (2 genomes)" expected result
        let genesums2 = map sum . transpose $ result
        assertEqual "gene-sums (2 genomes)" [1,1,1,1] genesums2
        let genomes3 = [[1,1,1,1],[0,0,0,0],[2,2,2,2]] :: [[Int]]
        -- genes from the last "celibate" genome are lost
        let result3 = filter (==2) . concat . map concat . flip map [0..100] $
                      \i -> flip evalRand (pureMT i) $
                      doCrossovers genomes (onePointCrossover 1.0)
        assertEqual "discard last genomes without a pair" [] result3
    , "simple crossover" ~: do
        let ones = replicate 8 1
        let zeros = replicate 8 0
        let genomes = [ones, zeros]
        let n = 1000
        assertEqual "exactly one crossover point" True $
                    all (<=2) . map (length . group) $
                        flip evalRand (pureMT 1) (doNCrossovers n genomes (onePointCrossover 1))
    , "simple crossover" ~: do
        let ones = replicate 8 1
        let zeros = replicate 8 0
        let genomes = [ones, zeros]
        let n = 1000
        assertEqual "exactly one crossover point" True $
                    all (<=2) . map (length . group) $
                        flip evalRand (pureMT 1) (doNCrossovers n genomes (onePointCrossover 1))
    , "two-point crossover" ~: do
        let ones = replicate 8 1
        let zeros = replicate 8 0
        let genomes = [ones, zeros]
        let n = 1000
        assertEqual "exactly two crossover point" True $
                    all (<=3) . map (length . group) $
                        flip evalRand (pureMT 1) (doNCrossovers n genomes (twoPointCrossover 1))
    , "uniform crossover" ~: do
        assertEqual "change all points"
                    ([[0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1]],[]) $
                    flip evalRand (pureMT 1) $
                             (uniformCrossover 1) [replicate 10 1,replicate 10 (0::Int)]
        assertEqual "change nothing"
                    ([[1,1,1,1,1,1,1,1,1,1],[0,0,0,0,0,0,0,0,0,0]],[]) $
                    flip evalRand (pureMT 1) $
                             (uniformCrossover 0) [replicate 10 1,replicate 10 (0::Int)]
        let n = 1000
        let mu = 0.5*n
        let sigma = sqrt(n*0.5*(1-0.5))  -- normal approx to binomial distribution
        let genomes = [ replicate (round n) 1
                      , replicate (round n) 0]
        let xover = uniformCrossover 0.5 :: CrossoverOp Double
        let mkChildren = doNCrossovers 1000 genomes xover :: Rand [Genome Double]
        let children = flip evalRand (pureMT 1) mkChildren :: [Genome Double]
        assertEqual "change approximately half" True $
                    all (\s -> (s >= mu - 4*sigma && s <= mu + 4*sigma)) . map sum $
                        children
    ]