module Neet.Species (
Species(..)
, SpecScore(..)
, newSpec
, TestResult(..)
, runFitTestWStrategy
, updateSpec
, maxDist
, speciesComplexity
, validateSpecies
) where
import Neet.Genome
import Neet.Parameters
import Data.MultiMap (MultiMap)
import qualified Data.MultiMap as MM
import Data.List (foldl')
import Data.Maybe
import Data.Traversable (Traversable)
import Control.Applicative ((<$>), (<*>))
data Species =
Species { specSize :: Int
, specOrgs :: [Genome]
, specScore :: SpecScore
, lastImprovement :: Int
}
data SpecScore = SpecScore { bestScore :: !Double, bestGen :: !Genome }
instance Show Species where
show (Species siz _ (SpecScore scr _) lastImprov) =
"Species {specSize = " ++ show siz ++
", specOrgs = <...>, bestScore = " ++ show scr ++
", bestGen = <...>" ++
", lastImprovement = " ++ show lastImprov ++ "}"
newSpec :: Genome -> [Genome] -> Species
newSpec gen gens = Species (length gens + 1) (gen:gens) (SpecScore 0 gen) 0
data TestResult =
TR { trScores :: MultiMap Double Genome
, trSS :: !SpecScore
, trAdj :: !Double
}
runFitTestWStrategy :: Functor f =>
(forall t. Traversable t => t Genome -> f (t Double)) -> Species -> f TestResult
runFitTestWStrategy strat spec = fmap (flip runFitTestWithScores spec) $ strat (specOrgs spec)
runFitTestWithScores :: [Double] -> Species -> TestResult
runFitTestWithScores fitList Species{..} = TR mmap ss (totF / dubSize)
where dubSize = fromIntegral specSize :: Double
(mmap, totF) = foldl' accumOne (MM.empty, 0) resses
resses = zip fitList specOrgs
accumOne (accM, accA) (score, g) = (MM.insert score g accM, accA + score)
ss = case MM.findMaxWithValues mmap of
Nothing -> error "(runFitTest) folding fitness resulted in empty map!"
Just (scr, (x:_)) -> SpecScore scr x
_ -> error "(runFitTest) MultiMap had a key with empty list!"
updateSpec :: SpecScore -> Species -> Species
updateSpec ss spec = spec { specScore = newScr
, lastImprovement = li
}
where oldScr = specScore spec
(newScr, li)
| bestScore ss > bestScore oldScr = (ss, 0)
| otherwise = (oldScr, lastImprovement spec + 1)
validateSpecies :: Species -> Maybe [String]
validateSpecies Species{..} = case orgErrs ++ goodSize of
[] -> Nothing
xs -> Just xs
where orgErrs = concat $ mapMaybe validateGenome specOrgs
goodSize
| specSize == length specOrgs = []
| otherwise = ["Species size differs from number of organisms"]
maxDist :: Parameters -> Species -> Double
maxDist ps Species{..} = maximum . map (uncurry (distance ps)) $ (,) <$> specOrgs <*> specOrgs
speciesComplexity :: Species -> Int
speciesComplexity spec = sum $ map genomeComplexity (specOrgs spec)