more gp loop/formatting

This commit is contained in:
Rowan Torbitzky-Lane 2025-02-21 00:50:10 -06:00
parent 0ebfb13e04
commit b57a802f11
5 changed files with 108 additions and 56 deletions

View File

@ -1,11 +1,13 @@
-- | The main file containing information about the GP loop and various population transformation functions. -- | The main file containing information about the GP loop and various population transformation functions.
module HushGP.GP where module HushGP.GP where
import HushGP.Genome
import HushGP.GP.PushArgs
import Control.Parallel.Strategies
import Control.Monad import Control.Monad
import Data.List (sort) import Control.Parallel.Strategies
import Data.List (sort, uncons)
import HushGP.GP.PushArgs
import HushGP.Genome
import HushGP.State
-- import Debug.Trace (trace, traceStack) -- import Debug.Trace (trace, traceStack)
-- | Using a PushArgs object, generates a population of the specified size with the -- | Using a PushArgs object, generates a population of the specified size with the
@ -17,21 +19,62 @@ generatePopulation pushArgs = do
-- | Evaluates a population of plushies with the error function passed in via PushArgs and sorts them. -- | Evaluates a population of plushies with the error function passed in via PushArgs and sorts them.
-- TODO: Need to make this runnable in parallel too. -- TODO: Need to make this runnable in parallel too.
evaluatePopulation :: PushArgs -> [Individual] -> [Individual] evaluatePopulation :: PushArgs -> ([[Gene]], [Gene]) -> [Individual] -> [Individual]
evaluatePopulation pushArgs population = sort $ zipWith updateIndividual (map (errorFunction pushArgs pushArgs (trainingData pushArgs) . plushy) population) population evaluatePopulation pushArgs passedTrainingData population = sort $ zipWith updateIndividual (map (errorFunction pushArgs pushArgs passedTrainingData . plushy) population) population
-- | A helper function used in evaluatePopulation. Takes a [Double] as the error scores and an individual. -- | A helper function used in evaluatePopulation. Takes a [Double] as the error scores and an individual.
-- Updates the error fields in an individual, and returns it. -- Updates the error fields in an individual, and returns it.
updateIndividual :: [Double] -> Individual -> Individual updateIndividual :: [Double] -> Individual -> Individual
updateIndividual errors ind = ind{totalFitness = Just (sum errors), fitnessCases = Just errors} updateIndividual errors ind = ind {totalFitness = Just (sum errors), fitnessCases = Just errors}
-- | The start of the gp loop. TODO: Make this more accurate later. -- | The start of the gp loop. TODO: Make this more accurate later.
gpLoop :: PushArgs -> IO () gpLoop :: PushArgs -> IO ()
gpLoop pushArgs = do gpLoop pushArgs = do
unEvaledPopulation <- generatePopulation pushArgs unEvaledPopulation <- generatePopulation pushArgs
let evaledPop = evaluatePopulation pushArgs unEvaledPopulation -- let evaledPop = evaluatePopulation pushArgs unEvaledPopulation
print evaledPop -- print evaledPop
print "gamer"
-- | The guts of the GP loop. Where the work gets done after the initialization happens -- | The guts of the GP loop. Where the work gets done after the initialization happens
-- in the main gpLoop function. -- in the main gpLoop function. The first Int holds the generation count. The second Int
-- gpLoop' -- holds the evaluation count. The list of Individuals is the population. The last parameter is
-- the training data (possibly downsampled).
gpLoop' :: PushArgs -> Int -> Int -> [Individual] -> ([[Gene]], [Gene]) -> IO ()
gpLoop' pushArgs generation evaluations population indexedTrainingData = do
print "Put information about each generation here."
when bestIndPassesDownsample $ print $ "Semi Success Generation: " <> show generation
let nextAction | (bestIndPassesDownsample && ((case totalFitness (updateIndividual (errorFunction epsilonPushArgs epsilonPushArgs indexedTrainingData (plushy bestInd)) bestInd) of (Just x) -> x; _ -> error "Error: Best downsample individual has no fitness!") <= solutionErrorThreshold epsilonPushArgs)) || (not (enableDownsampling epsilonPushArgs) && ((case totalFitness bestInd of (Just x) -> x; _ -> error "error: Best non-downsample individual has no fitness!") <= solutionErrorThreshold epsilonPushArgs)) =
do
print $ "Successful generation: " <> show generation
print $ "Successful plushy: " <> show (plushy bestInd)
print $ "Successful program: " <> show (plushyToPush $ plushy bestInd)
when (useSimplification epsilonPushArgs) $
do
let simplifiedPlushy = undefined -- TODO: simplification later
print "Total test error simplified: " <> undefined -- Implement later
print $ "Simplified plushy: " <> undefined -- show simplifiedPlushy
print $ "Simplified program: " <> undefined -- show plushyToPush simplifiedPlushy
| (not (enableDownsampling epsilonPushArgs) && (generation >= maxGenerations epsilonPushArgs)) || (enableDownsampling epsilonPushArgs && (evaluations >= (maxGenerations epsilonPushArgs * length population * length (fst indexedTrainingData)))) =
print "Incomplete Run, saving the best so far."
| otherwise = gpLoop' pushArgs (succ generation)
(evaluations + (populationSize pushArgs * length (fst $ trainingData pushArgs)) + (if generation `mod` downsampleParentsGens pushArgs == 0 then length parentReps * (length (fst indexedTrainingData) - length (fst $ trainingData pushArgs)) else 0) + (if bestIndPassesDownsample then length (fst indexedTrainingData) - length (fst $ trainingData pushArgs) else 0))
nextAction
where
-- \| This will have downsampling added to it later.
loopTrainData :: ([[Gene]], [Gene])
loopTrainData = indexedTrainingData
-- \| This will have downsampling functionality added later.
parentReps :: [Individual]
parentReps = []
-- \| This will have downsampling functionality added later.
repEvaluatedPop :: [Individual]
repEvaluatedPop = []
evaledPop :: [Individual]
evaledPop = evaluatePopulation pushArgs indexedTrainingData population
bestInd :: Individual
bestInd = case uncons evaledPop of Just (x, _) -> x; _ -> error "Error: Population is empty!"
bestIndPassesDownsample :: Bool
bestIndPassesDownsample = False -- TODO: fix this later
epsilonPushArgs :: PushArgs
epsilonPushArgs = pushArgs {epsilons = Nothing} -- TODO: And this
gpLoop' _ _ _ _ _ = error "How did this happen?"

View File

@ -77,7 +77,7 @@ data PushArgs = PushArgs
-- | Whether to use mutli-threading. -- | Whether to use mutli-threading.
useMultiThreading :: Bool, useMultiThreading :: Bool,
-- | Max total error for solutions. -- | Max total error for solutions.
solutionErrorThreshold :: Int, solutionErrorThreshold :: Double,
-- | Limit of push interpreter steps in push program evaluation. -- | Limit of push interpreter steps in push program evaluation.
stepLimit :: Int, stepLimit :: Int,
-- | For tournament selection, amount of individuals in each tournament. -- | For tournament selection, amount of individuals in each tournament.
@ -90,7 +90,9 @@ data PushArgs = PushArgs
umadRate :: Float, umadRate :: Float,
-- | Genetic operators and probabilities for their use, should sum to one -- | Genetic operators and probabilities for their use, should sum to one
-- Takes a Map of String -> Float where the string is the genetic operator -- Takes a Map of String -> Float where the string is the genetic operator
variation :: Map.Map String Float variation :: Map.Map String Float,
-- | The epsilons calculated for epsilon lexicase selection. Only used for epsilon lexicase selection.
epsilons :: Maybe [Double]
} }
-- | The default values for which all runs of Hush derive -- | The default values for which all runs of Hush derive
@ -128,12 +130,13 @@ defaultPushArgs = PushArgs {
simplificationSteps = 1000, simplificationSteps = 1000,
simplificationVerbose = False, simplificationVerbose = False,
useMultiThreading = False, -- False for now, change to True later. useMultiThreading = False, -- False for now, change to True later.
solutionErrorThreshold = 0, solutionErrorThreshold = 0.0,
ssxNotBmx = False, ssxNotBmx = False,
stepLimit = 1000, stepLimit = 1000,
tournamentSize = 5, tournamentSize = 5,
testingData = ([], []), testingData = ([], []),
trainingData = ([], []), trainingData = ([], []),
umadRate = 0.1, umadRate = 0.1,
variation = Map.fromList [("umad", 1.0)] variation = Map.fromList [("umad", 1.0)],
epsilons = Nothing
} }

View File

@ -3,20 +3,22 @@ module HushGP.Genome where
import Data.List import Data.List
import Data.List.Split import Data.List.Split
import Data.Map qualified as Map import Data.Map qualified as Map
import HushGP.GP.PushArgs
import HushGP.Instructions.Opens import HushGP.Instructions.Opens
import HushGP.State import HushGP.State
import HushGP.Utility import HushGP.Utility
import HushGP.GP.PushArgs
-- import HushGP.Instructions -- import HushGP.Instructions
-- import Debug.Trace -- import Debug.Trace
-- | The structure for an individual containing the genome, the totalFitness, and -- | The structure for an individual containing the genome, the totalFitness, and
-- the individual fitness cases for lexicase. -- the individual fitness cases for lexicase.
data Individual = Individual { data Individual = Individual
plushy :: [Gene], { plushy :: [Gene],
totalFitness :: Maybe Double, totalFitness :: Maybe Double,
fitnessCases :: Maybe [Double] fitnessCases :: Maybe [Double]
} deriving (Show, Eq) }
deriving (Show, Eq)
instance Ord Individual where instance Ord Individual where
ind0 <= ind1 = totalFitness ind0 <= totalFitness ind1 ind0 <= ind1 = totalFitness ind0 <= totalFitness ind1
@ -70,29 +72,31 @@ plushyToPush plushy = plushyToPush' (concatMap (\x -> if isOpenerList x then x <
-- | Internal function used to convert a plushy genome with opens in it into its push phenotype. -- | Internal function used to convert a plushy genome with opens in it into its push phenotype.
plushyToPush' :: [Gene] -> [Gene] -> [Gene] plushyToPush' :: [Gene] -> [Gene] -> [Gene]
plushyToPush' openPlushy push plushyToPush' openPlushy push
| null openPlushy = if any isOpen push | null openPlushy =
if any isOpen push
then plushyToPush' [Close] push then plushyToPush' [Close] push
else push else push
| firstPlushy == Close = if any isOpen push | firstPlushy == Close =
then plushyToPush' (drop 1 openPlushy) (if numOpen (push !! openIndex) == 1 then preOpen <> [Block postOpen] else preOpen <> [Block postOpen] <> [decOpen (Open (numOpen (push !! openIndex)))]) if any isOpen push
else plushyToPush' (drop 1 openPlushy) push then plushyToPush' (drop 1 openPlushy) (if numOpen (push !! openIndex) == 1 then preOpen <> [Block postOpen] else preOpen <> [Block postOpen] <> [decOpen (Open (numOpen (push !! openIndex)))])
else plushyToPush' (drop 1 openPlushy) push
| firstPlushy == Skip = | firstPlushy == Skip =
case uncons openPlushy of case uncons openPlushy of
Just (_, _ : xs) -> plushyToPush' xs push Just (_, _ : xs) -> plushyToPush' xs push
_ -> plushyToPush' (drop 1 openPlushy) push _ -> plushyToPush' (drop 1 openPlushy) push
| otherwise = plushyToPush' (drop 1 openPlushy) (push <> [firstPlushy]) | otherwise = plushyToPush' (drop 1 openPlushy) (push <> [firstPlushy])
where where
firstPlushy :: Gene firstPlushy :: Gene
firstPlushy firstPlushy =
= case uncons openPlushy of case uncons openPlushy of
Just (g, _) -> g Just (g, _) -> g
_ -> error "This shouldn't happen" _ -> error "This shouldn't happen"
postOpen :: [Gene] postOpen :: [Gene]
postOpen = reverse (takeWhile (not . isOpen) (reverse push)) postOpen = reverse (takeWhile (not . isOpen) (reverse push))
openIndex :: Int openIndex :: Int
openIndex = length push - length postOpen - 1 openIndex = length push - length postOpen - 1
numOpen :: Gene -> Int numOpen :: Gene -> Int
numOpen (Open n) = n numOpen (Open n) = n
numOpen _ = 0 numOpen _ = 0
preOpen :: [Gene] preOpen :: [Gene]
preOpen = take openIndex push preOpen = take openIndex push

View File

@ -40,15 +40,17 @@ noOpStateFuncBlock = StateFunc (instructionNoOpBlock, "instructionNoOpBlock")
-- | All of the instructions declared in all the instruction submodules -- | All of the instructions declared in all the instruction submodules
allInstructions :: [Gene] allInstructions :: [Gene]
allInstructions = allInstructions =
noOpStateFunc : noOpStateFuncBlock : allIntInstructions noOpStateFunc
<> allFloatInstructions : noOpStateFuncBlock
<> allBoolInstructions : allIntInstructions
<> allCharInstructions <> allFloatInstructions
<> allCodeInstructions <> allBoolInstructions
<> allExecInstructions <> allCharInstructions
<> allStringInstructions <> allCodeInstructions
<> allVectorIntInstructions <> allExecInstructions
<> allVectorFloatInstructions <> allStringInstructions
<> allVectorCharInstructions <> allVectorIntInstructions
<> allVectorStringInstructions <> allVectorFloatInstructions
<> allVectorBoolInstructions <> allVectorCharInstructions
<> allVectorStringInstructions
<> allVectorBoolInstructions

View File

@ -21,8 +21,8 @@ data Gene
| GeneVectorBool [Bool] | GeneVectorBool [Bool]
| GeneVectorString [String] | GeneVectorString [String]
| GeneVectorChar [Char] | GeneVectorChar [Char]
-- | State -> State is the function itself. String stores the name of the function. | -- | State -> State is the function itself. String stores the name of the function.
| StateFunc (State -> State, String) StateFunc (State -> State, String)
| PlaceInput Int | PlaceInput Int
| Close | Close
| Open Int | Open Int