packages feed

neet 0.3.1.0 → 0.4.0.0

raw patch · 4 files changed

+80/−36 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Neet.Species: runFitTest :: GenScorer a -> Species -> TestResult
- Neet.Species: trSol :: TestResult -> !(Maybe Genome)
+ Neet.Population: TrainMethod :: (forall t. Traversable t => t Genome -> f (t Double)) -> TrainMethod f
+ Neet.Population: newtype TrainMethod f
+ Neet.Population: pureTrain :: GenScorer a -> TrainMethod Identity
+ Neet.Population: tmGen :: TrainMethod f -> forall t. Traversable t => t Genome -> f (t Double)
+ Neet.Population: trainPure :: GenScorer a -> Population -> Population
+ Neet.Population: winTrain :: GenScorer a -> TrainMethod ((,) (First Genome))
+ Neet.Species: runFitTestWStrategy :: Functor f => (forall t. Traversable t => t Genome -> f (t Double)) -> Species -> f TestResult
- Neet.Population: trainN :: Int -> GenScorer a -> Population -> Population
+ Neet.Population: trainN :: (Applicative f, Monad f) => TrainMethod f -> Int -> Population -> f Population
- Neet.Population: trainOnce :: GenScorer a -> Population -> (Population, Maybe Genome)
+ Neet.Population: trainOnce :: Applicative f => TrainMethod f -> Population -> f Population
- Neet.Species: TR :: MultiMap Double Genome -> !SpecScore -> !Double -> !(Maybe Genome) -> TestResult
+ Neet.Species: TR :: MultiMap Double Genome -> !SpecScore -> !Double -> TestResult

Files

neet.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/  name:                neet-version:             0.3.1.0+version:             0.4.0.0 synopsis:            A NEAT library for Haskell -- description:          homepage:            https://github.com/raymoo/NEET
src/Neet/Examples/XOR.hs view
@@ -33,6 +33,8 @@ import Neet import Neet.Species +import Data.Monoid+ import qualified Data.Map.Strict as M  import System.Random@@ -118,7 +120,7 @@ xorLoop :: Population -> IO (Population, Genome) xorLoop pop = do   printInfo pop-  let (pop', mg) = trainOnce xorFit pop+  let (First mg, pop') = trainOnce (winTrain xorFit) pop   case mg of    Nothing -> xorLoop pop'    Just g -> return (pop',g)
src/Neet/Population.hs view
@@ -33,6 +33,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-} module Neet.Population (                          Population(..)                        , SpecId(..)@@ -45,8 +46,14 @@                        , newPop                          -- * Training                        , trainOnce+                       , TrainMethod(..)+                         -- ** TrainMethods+                       , pureTrain+                       , winTrain+                         -- ** Convenience                        , trainN                        , trainUntil+                       , trainPure                          -- * Statistics                        , speciesCount                          -- * Debugging@@ -58,15 +65,19 @@ import Neet.Parameters  import Data.MultiMap (MultiMap)+ import qualified Data.MultiMap as MM  import Data.Map (Map) import qualified Data.Map as M -import Data.List (foldl', maximumBy, sortBy, mapAccumL)+import Data.List (foldl', maximumBy, sortBy)  import Data.Maybe+import Data.Monoid +import Data.Functor.Identity+ import Control.Monad.Random import Control.Monad.Fresh.Class import Control.Monad.Trans.State@@ -74,6 +85,8 @@ import Control.Applicative import Control.Monad +import Data.Traversable+ import Control.Parallel.Strategies  import Data.Function@@ -99,6 +112,29 @@   deriving (Show)  +-- | Describes how to handle each species or genome when processing fitness,+-- allowing the addition of additional effects+newtype TrainMethod f =+  TrainMethod { tmGen :: forall t. Traversable t => t Genome -> f (t Double)+                -- ^ How to process each 'Genome' of a species into a score.+              }+++-- | Train method without any additional effects+pureTrain :: GenScorer a -> TrainMethod Identity+pureTrain gs = TrainMethod go+  where go gmap = Identity $ fmap (fitnessFunction gs . gScorer gs) gmap+++-- | Train method that possibly returns a solution+winTrain :: GenScorer a -> TrainMethod ((,) (First Genome))+winTrain gs = TrainMethod (traverse go)+  where go genome+          | winCriteria gs score = (First (Just genome), fitnessFunction gs score)+          | otherwise = (First Nothing, fitnessFunction gs score)+          where score = gScorer gs genome++ data PopContext =   PC { nextInno :: InnoId      , randGen  :: StdGen@@ -235,10 +271,15 @@           return Population{..}  +trainOnce :: Applicative f => TrainMethod f -> Population -> f Population+trainOnce method pop = fmap (flip trainOnceWFits pop) $ specRes+  where specRes = traverse (runFitTestWStrategy (tmGen method)) (popSpecs pop)++ -- | Advances the population one generation with the fitness function, possibly -- giving a solution.-trainOnce :: GenScorer a -> Population -> (Population, Maybe Genome)-trainOnce scorer pop = (generated, msolution)+trainOnceWFits :: Map SpecId TestResult -> Population -> Population+trainOnceWFits tResults pop = generated   where params = popParams pop          mParams = mutParams params@@ -271,12 +312,8 @@         oneEval = evalTuple2 r0 rseq          -- | Map to fitness data from runFitTest-        fits = M.map (\sp -> (sp, runFitTest scorer sp)) initSpecs `using` parTraversable oneEval+        fits = M.intersectionWith (,) initSpecs tResults `using` parTraversable oneEval -        msolution = go $ map (trSol . snd) $ M.elems fits-          where go [] = Nothing-                go (Just x:_) = Just x-                go (_:xs) = go xs          -- | Whether a species deserves to live (did it improve recently?)         eugenics :: SpecId -> (Species, TestResult) ->@@ -426,14 +463,19 @@   -- | Train the population n times. Values less than 1 return the original.-trainN :: Int -> GenScorer a -> Population -> Population-trainN n scorer p-  | n <= 0 = p-  | otherwise = applyN n (trainOnce scorer) p-  where applyN 0  _ !x = x-        applyN n' h !x = applyN (n' - 1) h (fst $ h x)+trainN :: (Applicative f, Monad f) =>+          TrainMethod f -> Int -> Population -> f Population+trainN tm n p+  | n <= 0 = return p+  | otherwise = applyN n (trainOnce tm) (return p)+  where applyN n' h !x = iterate (>>= h) x !! n'  +-- | Train without effects+trainPure :: GenScorer a -> Population -> Population+trainPure gs pop = runIdentity $ trainOnce (pureTrain gs) pop++ -- | Train until the provided goal is reached, or the max number -- of generations (first parameter) is reached. Possibly also returns a solution -- and the number of generations elapsed.@@ -442,9 +484,9 @@   | n <= 0 = (p, Nothing)   | otherwise = go n p   where go 0  !p' = (p', Nothing)-        go n' !p' = case trainOnce f p' of-                     (p'', Nothing) -> go (n' - 1) p''-                     (p'', Just g) -> (p'', Just (g, n - n'))+        go n' !p' = case trainOnce (winTrain f) p' of+                     (First Nothing, p'') -> go (n' - 1) p''+                     (First (Just g), p'') -> (p'', Just (g, n - n'))   -- | Gets the number of species
src/Neet/Species.hs view
@@ -29,6 +29,7 @@  {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-} module Neet.Species (                       Species(..)                     , SpecScore(..)@@ -36,7 +37,7 @@                     , newSpec                       -- * Update/Fitness                     , TestResult(..)-                    , runFitTest+                    , runFitTestWStrategy                     , updateSpec                       -- * Statistics                     , maxDist@@ -53,6 +54,7 @@ import qualified Data.MultiMap as MM import Data.List (foldl') import Data.Maybe+import Data.Traversable (Traversable)  import Control.Applicative ((<$>), (<*>)) @@ -86,28 +88,26 @@ -- | A result of evaluating a species data TestResult =   TR { trScores :: MultiMap Double Genome -- ^ The score of each organism-     , trSS     :: !SpecScore              -- ^ Result 'SpecScore'-     , trAdj    :: !Double                 -- ^ Total adjusted fitness-     , trSol    :: !(Maybe Genome)           -- ^ Possible Solution+     , trSS     :: !SpecScore             -- ^ Result 'SpecScore'+     , trAdj    :: !Double                -- ^ Total adjusted fitness      } -findMay :: (a -> Bool) -> [a] -> Maybe a-findMay _ [] = Nothing-findMay p (a:as)-  | p a = Just a-  | otherwise = findMay p as +-- | Output the result of testing fitness. The first argument tells how to process+-- each genome, with extra effects possible. If you don't need that, you can just+-- 'fmap' over your fitness function and put Identity over the result.+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) --- | Output the result of testing fitness. Last value is the total adjusted fitness-runFitTest :: GenScorer a -> Species -> TestResult-runFitTest GS{..} Species{..} = TR mmap ss (totF / dubSize) msolution++-- | "Helper function" for runFitTestWStrategy+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-        calcOne g = let !score = gScorer g in (score, g)-        resses = map calcOne specOrgs-        msolution = fmap snd . findMay (\pair -> winCriteria (fst pair)) $ resses-        accumOne (accM, accA) (score, g) = (MM.insert fit g accM, accA + fit)-          where fit = fitnessFunction score+        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