cleanup
This commit is contained in:
		
							parent
							
								
									6435f4aca2
								
							
						
					
					
						commit
						f79355e4c1
					
				
							
								
								
									
										34
									
								
								src/GA.hs
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								src/GA.hs
									
									
									
									
									
								
							@ -20,7 +20,7 @@
 | 
			
		||||
-- In order to use it for a certain problem, basically, you have to make your
 | 
			
		||||
-- solution type an instance of 'Individual' and then simply call the 'run'
 | 
			
		||||
-- function.
 | 
			
		||||
module GA (Environment, new, population, mutate, crossover1, crossover, nX, Evaluator, fitness, calc, Individual, GA.run, Tournament (..), N, R, Population, steps, bests, runTests) where
 | 
			
		||||
module GA (Environment, new, population, mutate, crossover1, crossover, nX, Fitness, getR, Evaluator, fitness,fitness', calc, Individual, GA.run, Tournament (..), N, R, Population, steps, bests, runTests) where
 | 
			
		||||
 | 
			
		||||
import Control.Arrow hiding (first, second)
 | 
			
		||||
import Data.List.NonEmpty ((<|))
 | 
			
		||||
@ -35,7 +35,6 @@ import System.Random.MWC (create, createSystemRandom)
 | 
			
		||||
import Test.QuickCheck hiding (sample, shuffle)
 | 
			
		||||
import Test.QuickCheck.Instances ()
 | 
			
		||||
import Test.QuickCheck.Monadic
 | 
			
		||||
import Debug.Trace as DB
 | 
			
		||||
 | 
			
		||||
-- TODO there should be a few 'shuffle's here
 | 
			
		||||
 | 
			
		||||
@ -84,13 +83,16 @@ class (Pretty e, Individual i) => Environment i e | e -> i where
 | 
			
		||||
-- |
 | 
			
		||||
--  An Evaluator that Individuals of type i can be evaluated by
 | 
			
		||||
--  It stores all information required to evaluate an individuals fitness
 | 
			
		||||
class (Individual i) => Evaluator i e where
 | 
			
		||||
class (Individual i, Fitness r) => Evaluator i e r | i -> e r where
 | 
			
		||||
  -- |
 | 
			
		||||
  --  An individual's fitness. Higher values are considered “better”.
 | 
			
		||||
  --
 | 
			
		||||
  --  We explicitely allow fitness values to be have any sign (see, for example,
 | 
			
		||||
  --  'proportionate1').
 | 
			
		||||
  fitness :: e -> i -> R
 | 
			
		||||
  fitness env i = getR ( fitness' env i)
 | 
			
		||||
 | 
			
		||||
  fitness' :: e -> i -> r
 | 
			
		||||
 | 
			
		||||
  -- TODO kinda hacky?!?
 | 
			
		||||
  calc :: e -> Population i -> IO e
 | 
			
		||||
@ -99,6 +101,12 @@ class (Individual i) => Evaluator i e where
 | 
			
		||||
 | 
			
		||||
class (Pretty i, Ord i) => Individual i
 | 
			
		||||
 | 
			
		||||
class (Show i) => Fitness i where
 | 
			
		||||
  getR :: i -> R
 | 
			
		||||
 | 
			
		||||
instance Fitness Double where
 | 
			
		||||
  getR d = d
 | 
			
		||||
 | 
			
		||||
-- |
 | 
			
		||||
-- Populations are just basic non-empty lists.
 | 
			
		||||
type Population i = NonEmpty i
 | 
			
		||||
@ -150,18 +158,18 @@ bestsBy' k f pop
 | 
			
		||||
 | 
			
		||||
-- |
 | 
			
		||||
-- The @k@ worst individuals in the population (and the rest of the population).
 | 
			
		||||
worst :: (Individual i, Evaluator i e) => e -> N -> Population i -> (NonEmpty i, [i])
 | 
			
		||||
worst :: (Individual i, Evaluator i e r) => e -> N -> Population i -> (NonEmpty i, [i])
 | 
			
		||||
worst e k = bestsBy k (negate . fitness e)
 | 
			
		||||
 | 
			
		||||
-- |
 | 
			
		||||
-- The @k@ best individuals in the population (and the rest of the population).
 | 
			
		||||
bests :: (Individual i, Evaluator i e) => e -> N -> Population i -> (NonEmpty i, [i])
 | 
			
		||||
bests :: (Individual i, Evaluator i e r) => e -> N -> Population i -> (NonEmpty i, [i])
 | 
			
		||||
bests e k = bestsBy k (fitness e)
 | 
			
		||||
 | 
			
		||||
-- TODO add top x percent parent selection (select n guys, sort by fitness first)
 | 
			
		||||
 | 
			
		||||
reproduce ::
 | 
			
		||||
  (Individual i, Environment i env, Evaluator i eval, SelectionType s) =>
 | 
			
		||||
  (Individual i, Environment i env, Evaluator i eval r, SelectionType s) =>
 | 
			
		||||
  eval ->
 | 
			
		||||
  env ->
 | 
			
		||||
  -- | Mechanism for selecting parents
 | 
			
		||||
@ -177,7 +185,7 @@ reproduce eval env selectT nParents pop = do
 | 
			
		||||
  return pop'
 | 
			
		||||
 | 
			
		||||
selectBest ::
 | 
			
		||||
  (Individual i, Evaluator i eval) =>
 | 
			
		||||
  (Individual i, Evaluator i eval r) =>
 | 
			
		||||
  eval ->
 | 
			
		||||
  -- | Elitism ratio @pElite@
 | 
			
		||||
  R ->
 | 
			
		||||
@ -198,7 +206,7 @@ selectBest eval pElite pop nPop = do
 | 
			
		||||
        else return $ elitists <> (fst $ bests eval (nPop - length elitists) (NE.fromList rest))
 | 
			
		||||
 | 
			
		||||
run ::
 | 
			
		||||
  (Individual i, Evaluator i eval, Environment i env, SelectionType s) =>
 | 
			
		||||
  (Individual i, Evaluator i eval r, Environment i env, SelectionType s) =>
 | 
			
		||||
  eval ->
 | 
			
		||||
  env ->
 | 
			
		||||
  -- | Mechanism for selecting parents
 | 
			
		||||
@ -210,7 +218,7 @@ run ::
 | 
			
		||||
  -- | Population size
 | 
			
		||||
  N ->
 | 
			
		||||
  Termination i ->
 | 
			
		||||
  Producer (Int, R) IO (Population i)
 | 
			
		||||
  Producer (Int, r) IO (Population i)
 | 
			
		||||
run eval env selectionType nParents pElite nPop term = do
 | 
			
		||||
  mwc <- liftIO createSystemRandom
 | 
			
		||||
  let smpl = ((sampleFrom mwc) :: RVar a -> IO a)
 | 
			
		||||
@ -227,7 +235,7 @@ run eval env selectionType nParents pElite nPop term = do
 | 
			
		||||
          withKids <- liftIO $ smpl $ reproduce eval env selectionType nParents pop
 | 
			
		||||
          eval <- liftIO $ calc eval withKids
 | 
			
		||||
          resPop <- liftIO $ smpl $ selectBest eval pElite withKids nPop
 | 
			
		||||
          let fBest = fitness eval $ NE.head $ fst $ bests eval 1 resPop
 | 
			
		||||
          let fBest = fitness' eval $ NE.head $ fst $ bests eval 1 resPop
 | 
			
		||||
          Pipes.yield (count, fBest)
 | 
			
		||||
          res <- runIter eval (count + 1) resPop smpl
 | 
			
		||||
          return res)
 | 
			
		||||
@ -240,7 +248,7 @@ run eval env selectionType nParents pElite nPop term = do
 | 
			
		||||
data Tournament = Tournament N
 | 
			
		||||
 | 
			
		||||
class SelectionType t where
 | 
			
		||||
  select :: (Individual i, Evaluator i e) => t -> N -> Population i -> e -> RVar (NonEmpty i)
 | 
			
		||||
  select :: (Individual i, Evaluator i e r) => t -> N -> Population i -> e -> RVar (NonEmpty i)
 | 
			
		||||
 | 
			
		||||
-- type Selection m i = N -> Population i -> m (NonEmpty i)
 | 
			
		||||
 | 
			
		||||
@ -250,7 +258,7 @@ instance SelectionType Tournament where
 | 
			
		||||
-- |
 | 
			
		||||
-- Selects one individual from the population using tournament selection.
 | 
			
		||||
tournament1 ::
 | 
			
		||||
  (Individual i, Evaluator i e) =>
 | 
			
		||||
  (Individual i, Evaluator i e r) =>
 | 
			
		||||
  e ->
 | 
			
		||||
  -- | Tournament size
 | 
			
		||||
  N ->
 | 
			
		||||
@ -321,7 +329,7 @@ instance Environment Integer IntTestEnviroment where
 | 
			
		||||
 | 
			
		||||
data NoData = NoData deriving (Eq)
 | 
			
		||||
 | 
			
		||||
instance Evaluator Integer NoData where
 | 
			
		||||
instance Evaluator Integer NoData Double where
 | 
			
		||||
  fitness _ = fromIntegral . negate
 | 
			
		||||
 | 
			
		||||
prop_children_asManyAsParents ::
 | 
			
		||||
 | 
			
		||||
@ -391,11 +391,25 @@ data LamdaExecutionEnv = LamdaExecutionEnv
 | 
			
		||||
    trainingData :: ([(Float, Float, Float, Float)], [IrisClass]),
 | 
			
		||||
    exTargetType :: TypeRep,
 | 
			
		||||
    -- todo: kindaHacky
 | 
			
		||||
    results :: Map TypeRequester R
 | 
			
		||||
    results :: Map TypeRequester FittnesRes
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
instance Evaluator TypeRequester LamdaExecutionEnv where
 | 
			
		||||
  fitness env tr = (results env) Map.! tr
 | 
			
		||||
data FittnesRes = FittnesRes
 | 
			
		||||
  { total :: R,
 | 
			
		||||
    fitnessTotal :: R,
 | 
			
		||||
    fitnessGeoMean :: R,
 | 
			
		||||
    fitnessMean :: R,
 | 
			
		||||
    accuracy :: Int,
 | 
			
		||||
    biasDist :: R,
 | 
			
		||||
    biasSize :: R
 | 
			
		||||
  }
 | 
			
		||||
  deriving (Show)
 | 
			
		||||
 | 
			
		||||
instance Fitness FittnesRes where
 | 
			
		||||
  getR = total
 | 
			
		||||
 | 
			
		||||
instance Evaluator TypeRequester LamdaExecutionEnv FittnesRes where
 | 
			
		||||
  fitness' env tr = (results env) Map.! tr
 | 
			
		||||
 | 
			
		||||
  calc env pop = do
 | 
			
		||||
    let toAdd = NE.filter (\k -> not (Map.member k (results env))) pop
 | 
			
		||||
@ -417,22 +431,64 @@ loadTrainingData lee@LamdaExecutionEnv {trainingData = (_ : _, _ : _)} = return
 | 
			
		||||
loadTrainingData lee@LamdaExecutionEnv {trainingData = (_ : _, [])} = return undefined
 | 
			
		||||
loadTrainingData lee@LamdaExecutionEnv {trainingData = ([], _ : _)} = return undefined
 | 
			
		||||
 | 
			
		||||
evalResults :: LamdaExecutionEnv -> [TypeRequester] -> Hint.InterpreterT IO [(TypeRequester, R)]
 | 
			
		||||
evalResults :: LamdaExecutionEnv -> [TypeRequester] -> Hint.InterpreterT IO [(TypeRequester, FittnesRes)]
 | 
			
		||||
evalResults ex trs = mapM (evalResult ex) trs
 | 
			
		||||
 | 
			
		||||
evalResult :: LamdaExecutionEnv -> TypeRequester -> Hint.InterpreterT IO (TypeRequester, R)
 | 
			
		||||
evalResult :: LamdaExecutionEnv -> TypeRequester -> Hint.InterpreterT IO (TypeRequester, FittnesRes)
 | 
			
		||||
evalResult ex tr = do
 | 
			
		||||
  Hint.setImports $ (map T.unpack (imports ex)) ++ ["Protolude"]
 | 
			
		||||
  Hint.unsafeSetGhcOption "-O2"
 | 
			
		||||
  result <- Hint.interpret (T.unpack (toLambdaExpressionS tr)) (Hint.as :: Float -> Float -> Float -> Float -> IrisClass)
 | 
			
		||||
  let res = map (\(a,b,c,d) -> result a b c d) (fst (trainingData ex))
 | 
			
		||||
  let res = map (\(a, b, c, d) -> result a b c d) (fst (trainingData ex))
 | 
			
		||||
  let resAndTarget = (zip (snd (trainingData ex)) res)
 | 
			
		||||
  let acc = (foldr (\ts s -> if ((fst ts) == (snd ts)) then s + 1 else s) 0 resAndTarget) :: R
 | 
			
		||||
  let biasWellDistributed = (foldr (*) 1 (map (\ty -> (foldr (\ts s -> if ((snd ts) == ty) then s + 1 else s) 1 resAndTarget)) ([minBound..maxBound] :: [IrisClass]):: [R])) ** (1/3)
 | 
			
		||||
  let biasSmall = exp ( - (fromIntegral (countTrsR tr)))
 | 
			
		||||
  let score = acc + (biasWellDistributed/5.1) + (biasSmall)
 | 
			
		||||
  return (tr, score)
 | 
			
		||||
  let acc = (foldr (\ts s -> if ((fst ts) == (snd ts)) then s + 1 else s) 0 resAndTarget) :: Int
 | 
			
		||||
  let biasWellDistributed = (foldr (*) 1 (map (\ty -> (foldr (\ts s -> if ((snd ts) == ty) then s + 1 else s) 1 resAndTarget)) ([minBound .. maxBound] :: [IrisClass]) :: [R])) ** (1 / 3) -- 1 (schlecht) bis 51 (gut)
 | 
			
		||||
  let biasSmall = exp (-(fromIntegral (countTrsR tr))) -- 0 (schlecht) bis 1 (gut)
 | 
			
		||||
  let fitness' = mean [meanOfAccuricyPerClass resAndTarget, geomeanOfDistributionAccuracy resAndTarget]
 | 
			
		||||
  let score = fitness' + (biasSmall - 1)
 | 
			
		||||
  return
 | 
			
		||||
    ( tr,
 | 
			
		||||
      FittnesRes
 | 
			
		||||
        { total = score,
 | 
			
		||||
          fitnessTotal = fitness',
 | 
			
		||||
          fitnessMean = meanOfAccuricyPerClass resAndTarget,
 | 
			
		||||
          fitnessGeoMean = geomeanOfDistributionAccuracy resAndTarget,
 | 
			
		||||
          accuracy = acc,
 | 
			
		||||
          biasDist = biasWellDistributed,
 | 
			
		||||
          biasSize = biasSmall
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
if' :: Bool -> a -> a -> a
 | 
			
		||||
if' True e _ = e
 | 
			
		||||
if' False _ e = e
 | 
			
		||||
 | 
			
		||||
meanOfAccuricyPerClass :: (Enum r, Bounded r, Eq r) => [(r, r)] -> R
 | 
			
		||||
meanOfAccuricyPerClass results = mean $ map (accuracyInClass results) [minBound .. maxBound]
 | 
			
		||||
 | 
			
		||||
geomeanOfAccuricyPerClass :: (Enum r, Bounded r, Eq r) => [(r, r)] -> R
 | 
			
		||||
geomeanOfAccuricyPerClass results = geomean $ map (accuracyInClass results) [minBound .. maxBound]
 | 
			
		||||
 | 
			
		||||
geomeanOfDistributionAccuracy :: (Enum r, Bounded r, Eq r) => [(r, r)] -> R
 | 
			
		||||
geomeanOfDistributionAccuracy results = geomean $ map (distributionAccuracyForClass results) [minBound .. maxBound]
 | 
			
		||||
 | 
			
		||||
distributionAccuracyForClass :: (Eq r) => [(r, r)] -> r -> R
 | 
			
		||||
distributionAccuracyForClass results clas = (1 - (min 1 (fromIntegral (abs ((length (inResClass results clas)) - (length (inClass results clas)))) / fromIntegral (length (inClass results clas))))) * 100
 | 
			
		||||
 | 
			
		||||
mean :: (Show f, Floating f) => [f] -> f
 | 
			
		||||
mean values = (sum values) * (1 / (fromIntegral (length values)))
 | 
			
		||||
 | 
			
		||||
geomean :: (Show f, Floating f) => [f] -> f
 | 
			
		||||
geomean values = (product values) ** (1 / (fromIntegral (length values)))
 | 
			
		||||
 | 
			
		||||
accuracyInClass :: (Eq r) => [(r, r)] -> r -> R
 | 
			
		||||
accuracyInClass results clas = ((accuracy'(inResClass results clas)) * 100) / fromIntegral (length (inClass results clas))
 | 
			
		||||
 | 
			
		||||
inClass :: (Eq r) => [(r, r)] -> r -> [(r, r)]
 | 
			
		||||
inClass results clas = (filter ((clas ==) . fst) results)
 | 
			
		||||
 | 
			
		||||
inResClass :: (Eq r) => [(r, r)] -> r -> [(r, r)]
 | 
			
		||||
inResClass results clas = (filter ((clas ==) . snd) results)
 | 
			
		||||
 | 
			
		||||
accuracy' :: (Eq r) => [(r, r)] -> R
 | 
			
		||||
accuracy' results = fromIntegral $ length (filter (\(target, res) -> (res == target)) results)
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,7 @@ main =
 | 
			
		||||
    hSetBuffering stdout NoBuffering
 | 
			
		||||
    let env = irisLE
 | 
			
		||||
    let selType = Tournament 3
 | 
			
		||||
    let run' = run irisLEE env selType 40 (5 / 100) (populationSize opts) (steps (iterations opts)) :: Producer (Int, R) IO (Population TypeRequester)
 | 
			
		||||
    let run' = run irisLEE env selType 40 (5 / 100) (populationSize opts) (steps (iterations opts))
 | 
			
		||||
    pop' <-
 | 
			
		||||
      runEffect (for run' logCsv)
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@ main =
 | 
			
		||||
    mapM_ (format irisLE) res
 | 
			
		||||
  where
 | 
			
		||||
    format irisL s = do
 | 
			
		||||
      let f = fitness irisL s
 | 
			
		||||
      let f = fitness' irisL s
 | 
			
		||||
      putErrText $ show f <> "\n" <> pretty s
 | 
			
		||||
    logCsv = putText . csv
 | 
			
		||||
    csv (t, f) = show t <> " " <> show f
 | 
			
		||||
 | 
			
		||||
@ -159,8 +159,8 @@ prioOf' p (Just _) Nothing = lowestPriority p + 2
 | 
			
		||||
prioOf' p Nothing (Just _) = lowestPriority p + 2
 | 
			
		||||
prioOf' p (Just s) (Just t) = prioOf p s t
 | 
			
		||||
 | 
			
		||||
instance Evaluator Assignment Priorities where
 | 
			
		||||
  fitness prio assment =
 | 
			
		||||
instance Evaluator Assignment Priorities R where
 | 
			
		||||
  fitness' prio assment =
 | 
			
		||||
    negate . sum $ fromIntegral . uncurry (prioOf' prio) <$> assment
 | 
			
		||||
 | 
			
		||||
-- |
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user