From 373dd967eedebc494dd013f4b1fda80861840fbd Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 30 Aug 2024 14:16:25 +0300 Subject: [PATCH] Implemented parallel evaluation of the organisms for single pole balancing experiment --- Makefile | 23 +++++ examples/pole/cartpole.go | 146 +--------------------------- examples/pole/cartpole_parallel.go | 144 +++++++++++++++++++++++++++ examples/pole/cartpole_test.go | 2 +- examples/pole/common.go | 150 +++++++++++++++++++++++++++++ examples/pole2/common.go | 22 ++--- examples/pole2/generalization.go | 2 +- executor.go | 23 +++-- 8 files changed, 345 insertions(+), 167 deletions(-) create mode 100644 examples/pole/cartpole_parallel.go create mode 100644 examples/pole/common.go diff --git a/Makefile b/Makefile index e76a89a..6e44c8c 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,18 @@ run-cartpole-two-markov: -trials $(TRIALS_NUMBER) \ -log_level $(LOG_LEVEL) + +# The target to run double-pole Markov experiment in parallel objective +# function evaluation mode +# +run-cartpole-two-parallel-markov: + $(GORUN) executor.go -out $(OUT_DIR)/pole2_markov_parallel \ + -context $(DATA_DIR)/pole2_markov.neat \ + -genome $(DATA_DIR)/pole2_markov_startgenes \ + -experiment cart_2pole_markov_parallel \ + -trials $(TRIALS_NUMBER) \ + -log_level $(LOG_LEVEL) + # The target to run single-pole experiment # run-cartpole: @@ -53,6 +65,17 @@ run-cartpole: -trials $(TRIALS_NUMBER) \ -log_level $(LOG_LEVEL) +# The target to run single-pole experiment in parallel objective +# function evaluation mode +# +run-cartpole-parallel: + $(GORUN) executor.go -out $(OUT_DIR)/pole1_parallel \ + -context $(DATA_DIR)/pole1_150.neat \ + -genome $(DATA_DIR)/pole1startgenes \ + -experiment cart_pole_parallel \ + -trials 100 \ + -log_level $(LOG_LEVEL) + # The target to run disconnected XOR experiment # run-xor-disconnected: diff --git a/examples/pole/cartpole.go b/examples/pole/cartpole.go index 23c252d..3c9bcf6 100644 --- a/examples/pole/cartpole.go +++ b/examples/pole/cartpole.go @@ -7,16 +7,12 @@ import ( "github.com/yaricom/goNEAT/v4/experiment/utils" "github.com/yaricom/goNEAT/v4/neat" "github.com/yaricom/goNEAT/v4/neat/genetics" - "github.com/yaricom/goNEAT/v4/neat/network" - "math" - "math/rand" ) -const twelveDegrees = 12.0 * math.Pi / 180.0 - type cartPoleGenerationEvaluator struct { // The output path to store execution results OutputPath string + // The flag to indicate if cart emulator should be started from random position RandomStart bool // The number of emulation steps to be done balancing pole to win @@ -41,7 +37,7 @@ func (e *cartPoleGenerationEvaluator) GenerationEvaluate(ctx context.Context, po } // Evaluate each organism on a test for _, org := range pop.Organisms { - res, err := e.orgEvaluate(org) + res, err := OrganismEvaluate(org, e.WinBalancingSteps, e.RandomStart) if err != nil { return err } @@ -98,141 +94,3 @@ func (e *cartPoleGenerationEvaluator) GenerationEvaluate(ctx context.Context, po return nil } - -// orgEvaluate evaluates provided organism for cart pole balancing task -func (e *cartPoleGenerationEvaluator) orgEvaluate(organism *genetics.Organism) (bool, error) { - phenotype, err := organism.Phenotype() - if err != nil { - return false, err - } - - // Try to balance a pole now - if fitness, err := e.runCart(phenotype); err != nil { - return false, nil - } else { - organism.Fitness = float64(fitness) - } - - if neat.LogLevel == neat.LogLevelDebug { - neat.DebugLog(fmt.Sprintf("Organism #%3d\tfitness: %f", organism.Genotype.Id, organism.Fitness)) - } - - // Decide if it's a winner - if organism.Fitness >= float64(e.WinBalancingSteps) { - organism.IsWinner = true - } - - // adjust fitness to be in range [0;1] - if organism.IsWinner { - organism.Fitness = 1.0 - organism.Error = 0.0 - } else if organism.Fitness == 0 { - organism.Error = 1.0 - } else { - // we use logarithmic scale because most cart runs fail to early within ~100 steps, but - // we test against 500'000 balancing steps - logSteps := math.Log(float64(e.WinBalancingSteps)) - organism.Error = (logSteps - math.Log(organism.Fitness)) / logSteps - organism.Fitness = 1.0 - organism.Error - } - - return organism.IsWinner, nil -} - -// runCart runs the cart emulation and return number of emulation steps pole was balanced -func (e *cartPoleGenerationEvaluator) runCart(net *network.Network) (steps int, err error) { - var x float64 /* cart position, meters */ - var xDot float64 /* cart velocity */ - var theta float64 /* pole angle, radians */ - var thetaDot float64 /* pole angular velocity */ - if e.RandomStart { - /*set up random start state*/ - x = float64(rand.Int31()%4800)/1000.0 - 2.4 - xDot = float64(rand.Int31()%2000)/1000.0 - 1 - theta = float64(rand.Int31()%400)/1000.0 - .2 - thetaDot = float64(rand.Int31()%3000)/1000.0 - 1.5 - } - - netDepth, err := net.MaxActivationDepthWithCap(0) // The max depth of the network to be activated - if err != nil { - neat.WarnLog(fmt.Sprintf( - "Failed to estimate maximal depth of the network with loop.\nUsing default depth: %d", netDepth)) - } else if netDepth == 0 { - // possibly disconnected - return minimal fitness score - return 1, nil - } - - in := make([]float64, 5) - for steps = 0; steps < e.WinBalancingSteps; steps++ { - /*-- set up the input layer based on the four inputs --*/ - in[0] = 1.0 // Bias - in[1] = (x + 2.4) / 4.8 - in[2] = (xDot + .75) / 1.5 - in[3] = (theta + twelveDegrees) / .41 - in[4] = (thetaDot + 1.0) / 2.0 - if err = net.LoadSensors(in); err != nil { - return 0, err - } - - /*-- activate the network based on the input --*/ - if res, err := net.ForwardSteps(netDepth); !res { - //If it loops, exit returning only fitness of 1 step - neat.DebugLog(fmt.Sprintf("Failed to activate Network, reason: %s", err)) - return 1, nil - } - /*-- decide which way to push via which output unit is greater --*/ - action := 1 - if net.Outputs[0].Activation > net.Outputs[1].Activation { - action = 0 - } - /*--- Apply action to the simulated cart-pole ---*/ - x, xDot, theta, thetaDot = e.doAction(action, x, xDot, theta, thetaDot) - - /*--- Check for failure. If so, return steps ---*/ - if x < -2.4 || x > 2.4 || theta < -twelveDegrees || theta > twelveDegrees { - return steps, nil - } - } - return steps, nil -} - -// doAction was taken directly from the pole simulator written by Richard Sutton and Charles Anderson. -// This simulator uses normalized, continuous inputs instead of discretizing the input space. -/*---------------------------------------------------------------------- -Takes an action (0 or 1) and the current values of the -four state variables and updates their values by estimating the state -TAU seconds later. -----------------------------------------------------------------------*/ -func (e *cartPoleGenerationEvaluator) doAction(action int, x, xDot, theta, thetaDot float64) (xRet, xDotRet, thetaRet, thetaDotRet float64) { - // The cart pole configuration values - const Gravity = 9.8 - const MassCart = 1.0 - const MassPole = 0.5 - const TotalMass = MassPole + MassCart - const Length = 0.5 /* actually half the pole's length */ - const PoleMassLength = MassPole * Length - const ForceMag = 10.0 - const Tau = 0.02 /* seconds between state updates */ - const FourThirds = 1.3333333333333 - - force := -ForceMag - if action > 0 { - force = ForceMag - } - cosTheta := math.Cos(theta) - sinTheta := math.Sin(theta) - - temp := (force + PoleMassLength*thetaDot*thetaDot*sinTheta) / TotalMass - - thetaAcc := (Gravity*sinTheta - cosTheta*temp) / (Length * (FourThirds - MassPole*cosTheta*cosTheta/TotalMass)) - - xAcc := temp - PoleMassLength*thetaAcc*cosTheta/TotalMass - - /*** Update the four state variables, using Euler's method. ***/ - xRet = x + Tau*xDot - xDotRet = xDot + Tau*xAcc - thetaRet = theta + Tau*thetaDot - thetaDotRet = thetaDot + Tau*thetaAcc - - return xRet, xDotRet, thetaRet, thetaDotRet -} diff --git a/examples/pole/cartpole_parallel.go b/examples/pole/cartpole_parallel.go new file mode 100644 index 0000000..21d629f --- /dev/null +++ b/examples/pole/cartpole_parallel.go @@ -0,0 +1,144 @@ +package pole + +import ( + "context" + "fmt" + "github.com/yaricom/goNEAT/v4/experiment" + "github.com/yaricom/goNEAT/v4/experiment/utils" + "github.com/yaricom/goNEAT/v4/neat" + "github.com/yaricom/goNEAT/v4/neat/genetics" + "sync" +) + +type cartPoleParallelGenerationEvaluator struct { + cartPoleGenerationEvaluator +} + +type parallelEvaluationResult struct { + genomeId int + fitness float64 + error float64 + winner bool + err error +} + +// NewCartPoleParallelGenerationEvaluator is to create generations evaluator for single-pole balancing experiment. +// This experiment performs evolution on single pole balancing task in order to produce appropriate genome. +func NewCartPoleParallelGenerationEvaluator(outDir string, randomStart bool, winBalanceSteps int) experiment.GenerationEvaluator { + return &cartPoleParallelGenerationEvaluator{ + cartPoleGenerationEvaluator{ + OutputPath: outDir, + RandomStart: randomStart, + WinBalancingSteps: winBalanceSteps, + }, + } +} + +// GenerationEvaluate evaluates one epoch for given population and prints results into output directory if any. +func (e *cartPoleParallelGenerationEvaluator) GenerationEvaluate(ctx context.Context, pop *genetics.Population, epoch *experiment.Generation) error { + options, ok := neat.FromContext(ctx) + if !ok { + return neat.ErrNEATOptionsNotFound + } + + organismMapping := make(map[int]*genetics.Organism) + + popSize := len(pop.Organisms) + resChan := make(chan parallelEvaluationResult, popSize) + // The wait group to wait for all GO routines + var wg sync.WaitGroup + + // Evaluate each organism in generation + for _, org := range pop.Organisms { + if _, ok = organismMapping[org.Genotype.Id]; ok { + return fmt.Errorf("organism with %d already exists in mapping", org.Genotype.Id) + } + organismMapping[org.Genotype.Id] = org + wg.Add(1) + + // run in separate GO thread + go func(organism *genetics.Organism, resChan chan<- parallelEvaluationResult, wg *sync.WaitGroup) { + defer wg.Done() + + // create simulator and evaluate + winner, err := OrganismEvaluate(organism, e.WinBalancingSteps, e.RandomStart) + if err != nil { + resChan <- parallelEvaluationResult{err: err} + return + } + + // create result + result := parallelEvaluationResult{ + genomeId: organism.Genotype.Id, + fitness: organism.Fitness, + error: organism.Error, + winner: winner, + } + resChan <- result + + }(org, resChan, &wg) + } + + // wait for evaluation results + wg.Wait() + close(resChan) + + for result := range resChan { + if result.err != nil { + return result.err + } + // find and update original organism + org, ok := organismMapping[result.genomeId] + if ok { + org.Fitness = result.fitness + org.Error = result.error + } else { + return fmt.Errorf("organism not found in mapping for id: %d", result.genomeId) + } + + if result.winner && (epoch.Champion == nil || org.Fitness > epoch.Champion.Fitness) { + // This will be winner in Markov case + epoch.Solved = true + epoch.WinnerNodes = len(org.Genotype.Nodes) + epoch.WinnerGenes = org.Genotype.Extrons() + epoch.WinnerEvals = options.PopSize*epoch.Id + org.Genotype.Id + epoch.Champion = org + org.IsWinner = true + } + } + + // Fill statistics about current epoch + epoch.FillPopulationStatistics(pop) + + // Only print to file every print_every generation + if epoch.Solved || epoch.Id%options.PrintEvery == 0 { + if _, err := utils.WritePopulationPlain(e.OutputPath, pop, epoch); err != nil { + neat.ErrorLog(fmt.Sprintf("Failed to dump population, reason: %s\n", err)) + return err + } + } + + if epoch.Solved { + // print winner organism's statistics + org := epoch.Champion + utils.PrintActivationDepth(org, true) + + genomeFile := "pole1_winner_genome" + // Prints the winner organism to file! + if orgPath, err := utils.WriteGenomePlain(genomeFile, e.OutputPath, org, epoch); err != nil { + neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism's genome, reason: %s\n", err)) + } else { + neat.InfoLog(fmt.Sprintf("Generation #%d winner's genome dumped to: %s\n", epoch.Id, orgPath)) + } + + // Prints the winner organism's phenotype to the Cytoscape JSON file! + if orgPath, err := utils.WriteGenomeCytoscapeJSON(genomeFile, e.OutputPath, org, epoch); err != nil { + neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism's phenome Cytoscape JSON graph, reason: %s\n", err)) + } else { + neat.InfoLog(fmt.Sprintf("Generation #%d winner's phenome Cytoscape JSON graph dumped to: %s\n", + epoch.Id, orgPath)) + } + } + + return nil +} diff --git a/examples/pole/cartpole_test.go b/examples/pole/cartpole_test.go index 2fa14b5..578a2db 100644 --- a/examples/pole/cartpole_test.go +++ b/examples/pole/cartpole_test.go @@ -12,7 +12,7 @@ import ( "time" ) -// The integration test running running over multiple iterations +// The integration test running over multiple iterations func TestCartPoleGenerationEvaluator_GenerationEvaluate(t *testing.T) { if testing.Short() { t.Skip("skipping test in short Unit Test mode.") diff --git a/examples/pole/common.go b/examples/pole/common.go new file mode 100644 index 0000000..06cbb42 --- /dev/null +++ b/examples/pole/common.go @@ -0,0 +1,150 @@ +package pole + +import ( + "fmt" + "github.com/yaricom/goNEAT/v4/neat" + "github.com/yaricom/goNEAT/v4/neat/genetics" + "github.com/yaricom/goNEAT/v4/neat/network" + "math" + "math/rand" +) + +const twelveDegrees = 12.0 * math.Pi / 180.0 + +// OrganismEvaluate evaluates provided organism for cart pole balancing task +func OrganismEvaluate(organism *genetics.Organism, winnerBalancingSteps int, randomStart bool) (bool, error) { + phenotype, err := organism.Phenotype() + if err != nil { + return false, err + } + + // Try to balance a pole now + if fitness, err := runCart(phenotype, winnerBalancingSteps, randomStart); err != nil { + return false, nil + } else { + organism.Fitness = float64(fitness) + } + + if neat.LogLevel == neat.LogLevelDebug { + neat.DebugLog(fmt.Sprintf("Organism #%3d\tfitness: %f", organism.Genotype.Id, organism.Fitness)) + } + + // Decide if it's a winner + if organism.Fitness >= float64(winnerBalancingSteps) { + organism.IsWinner = true + } + + // adjust fitness to be in range [0;1] + if organism.IsWinner { + organism.Fitness = 1.0 + organism.Error = 0.0 + } else if organism.Fitness == 0 { + organism.Error = 1.0 + } else { + // we use logarithmic scale because most cart runs fail to early within ~100 steps, but + // we test against 500'000 balancing steps + logSteps := math.Log(float64(winnerBalancingSteps)) + organism.Error = (logSteps - math.Log(organism.Fitness)) / logSteps + organism.Fitness = 1.0 - organism.Error + } + + return organism.IsWinner, nil +} + +// runCart runs the cart emulation and return number of emulation steps pole was balanced +func runCart(net *network.Network, winnerBalancingSteps int, randomStart bool) (steps int, err error) { + var x float64 /* cart position, meters */ + var xDot float64 /* cart velocity */ + var theta float64 /* pole angle, radians */ + var thetaDot float64 /* pole angular velocity */ + if randomStart { + /*set up random start state*/ + x = float64(rand.Int31()%4800)/1000.0 - 2.4 + xDot = float64(rand.Int31()%2000)/1000.0 - 1 + theta = float64(rand.Int31()%400)/1000.0 - .2 + thetaDot = float64(rand.Int31()%3000)/1000.0 - 1.5 + } + + netDepth, err := net.MaxActivationDepthWithCap(0) // The max depth of the network to be activated + if err != nil { + neat.WarnLog(fmt.Sprintf( + "Failed to estimate maximal depth of the network with loop.\nUsing default depth: %d", netDepth)) + } else if netDepth == 0 { + // possibly disconnected - return minimal fitness score + return 1, nil + } + + in := make([]float64, 5) + for steps = 0; steps < winnerBalancingSteps; steps++ { + /*-- set up the input layer based on the four inputs --*/ + in[0] = 1.0 // Bias + in[1] = (x + 2.4) / 4.8 + in[2] = (xDot + .75) / 1.5 + in[3] = (theta + twelveDegrees) / .41 + in[4] = (thetaDot + 1.0) / 2.0 + if err = net.LoadSensors(in); err != nil { + return 0, err + } + + /*-- activate the network based on the input --*/ + if res, err := net.ForwardSteps(netDepth); !res { + //If it loops, exit returning only fitness of 1 step + neat.DebugLog(fmt.Sprintf("Failed to activate Network, reason: %s", err)) + return 1, nil + } + /*-- decide which way to push via which output unit is greater --*/ + action := 1 + if net.Outputs[0].Activation > net.Outputs[1].Activation { + action = 0 + } + /*--- Apply action to the simulated cart-pole ---*/ + x, xDot, theta, thetaDot = doAction(action, x, xDot, theta, thetaDot) + + /*--- Check for failure. If so, return steps ---*/ + if x < -2.4 || x > 2.4 || theta < -twelveDegrees || theta > twelveDegrees { + return steps, nil + } + } + return steps, nil +} + +// doAction was taken directly from the pole simulator written by Richard Sutton and Charles Anderson. +// This simulator uses normalized, continuous inputs instead of making the input space discrete. +/*---------------------------------------------------------------------- +Takes an action (0 or 1) and the current values of the +four state variables and updates their values by estimating the state +TAU seconds later. +----------------------------------------------------------------------*/ +func doAction(action int, x, xDot, theta, thetaDot float64) (xRet, xDotRet, thetaRet, thetaDotRet float64) { + // The cart pole configuration values + const Gravity = 9.8 + const MassCart = 1.0 + const MassPole = 0.5 + const TotalMass = MassPole + MassCart + const Length = 0.5 /* actually half the pole's length */ + const PoleMassLength = MassPole * Length + const ForceMag = 10.0 + const Tau = 0.02 /* seconds between state updates */ + const FourThirds = 1.3333333333333 + + force := -ForceMag + if action > 0 { + force = ForceMag + } + cosTheta := math.Cos(theta) + sinTheta := math.Sin(theta) + + temp := (force + PoleMassLength*thetaDot*thetaDot*sinTheta) / TotalMass + + thetaAcc := (Gravity*sinTheta - cosTheta*temp) / (Length * (FourThirds - MassPole*cosTheta*cosTheta/TotalMass)) + + xAcc := temp - PoleMassLength*thetaAcc*cosTheta/TotalMass + + /*** Update the four state variables, using Euler's method. ***/ + xRet = x + Tau*xDot + xDotRet = xDot + Tau*xAcc + thetaRet = theta + Tau*thetaDot + thetaDotRet = thetaDot + Tau*thetaAcc + + return xRet, xDotRet, thetaRet, thetaDotRet +} diff --git a/examples/pole2/common.go b/examples/pole2/common.go index 3bb5217..4d586b3 100644 --- a/examples/pole2/common.go +++ b/examples/pole2/common.go @@ -32,8 +32,8 @@ const ( DiscreteAction ) -// CartPole The structure to describe cart pole emulation -type CartPole struct { +// CartDoublePole The structure to describe cart pole emulation +type CartDoublePole struct { // The flag to indicate that we are executing Markov experiment setup (known velocities information) isMarkov bool // Flag that we are looking at the champion in Non-Markov experiment @@ -57,13 +57,13 @@ type CartPole struct { } // NewCartPole If markov is false, then velocity information will be withheld from the network population (non-Markov) -func NewCartPole(markov bool) *CartPole { - return &CartPole{ +func NewCartPole(markov bool) *CartDoublePole { + return &CartDoublePole{ isMarkov: markov, } } -func (p *CartPole) evalNet(net *network.Network, actionType ActionType) (steps float64, err error) { +func (p *CartDoublePole) evalNet(net *network.Network, actionType ActionType) (steps float64, err error) { nonMarkovMax := nonMarkovGeneralizationMaxSteps if p.nonMarkovLong { nonMarkovMax = nonMarkovLongMaxSteps @@ -202,7 +202,7 @@ func (p *CartPole) evalNet(net *network.Network, actionType ActionType) (steps f } } -func (p *CartPole) performAction(action, stepNum float64) { +func (p *CartDoublePole) performAction(action, stepNum float64) { const TAU = 0.01 // ∆t = 0.01s /*--- Apply action to the simulated cart-pole ---*/ @@ -230,7 +230,7 @@ func (p *CartPole) performAction(action, stepNum float64) { } } -func (p *CartPole) step(action float64, st [6]float64, derivs *[6]float64) { +func (p *CartDoublePole) step(action float64, st [6]float64, derivs *[6]float64) { const Mup = 0.000002 const Gravity = -9.8 const ForceMag = 10.0 // [N] @@ -266,7 +266,7 @@ func (p *CartPole) step(action float64, st [6]float64, derivs *[6]float64) { derivs[5] = -0.75 * (derivs[1]*cosTheta2 + gSinTheta2 + temp2) / Length2 } -func (p *CartPole) rk4(f float64, y, dydx [6]float64, yout *[6]float64, tau float64) { +func (p *CartDoublePole) rk4(f float64, y, dydx [6]float64, yout *[6]float64, tau float64) { var yt, dym, dyt [6]float64 hh := tau * 0.5 h6 := tau / 6.0 @@ -301,7 +301,7 @@ func (p *CartPole) rk4(f float64, y, dydx [6]float64, yout *[6]float64, tau floa } // Check if simulation goes outside of bounds -func (p *CartPole) outsideBounds() bool { +func (p *CartDoublePole) outsideBounds() bool { const failureAngle = thirtySixDegrees return p.state[0] < -2.4 || @@ -312,7 +312,7 @@ func (p *CartPole) outsideBounds() bool { p.state[4] > failureAngle } -func (p *CartPole) resetState() { +func (p *CartDoublePole) resetState() { if p.isMarkov { // Clear all fitness records p.cartPosSum = 0.0 @@ -330,7 +330,7 @@ func (p *CartPole) resetState() { } // OrganismEvaluate method evaluates fitness of the organism for cart double pole-balancing task -func OrganismEvaluate(organism *genetics.Organism, cartPole *CartPole, actionType ActionType) (winner bool, err error) { +func OrganismEvaluate(organism *genetics.Organism, cartPole *CartDoublePole, actionType ActionType) (winner bool, err error) { // Try to balance a pole now phenotype, err := organism.Phenotype() if err != nil { diff --git a/examples/pole2/generalization.go b/examples/pole2/generalization.go index 907df03..47eba05 100644 --- a/examples/pole2/generalization.go +++ b/examples/pole2/generalization.go @@ -19,7 +19,7 @@ import ( // and its angular velocity ∆θ2/∆t are set to zero. The GS is then defined as the number of successful runs // from the 625 initial conditions and an individual is defined as a solution if it reaches a generalization // score of 200 or more. -func EvaluateOrganismGeneralization(species []*genetics.Species, cartPole *CartPole, actionType ActionType) (*genetics.Organism, error) { +func EvaluateOrganismGeneralization(species []*genetics.Species, cartPole *CartDoublePole, actionType ActionType) (*genetics.Organism, error) { // Sort the species by max organism fitness in descending order - the highest fitness first sortedSpecies := make([]*genetics.Species, len(species)) copy(sortedSpecies, species) diff --git a/executor.go b/executor.go index cf342d3..c47a368 100644 --- a/executor.go +++ b/executor.go @@ -84,7 +84,7 @@ func main() { } // create experiment - expt := experiment.Experiment{ + exp := experiment.Experiment{ Id: 0, Trials: make(experiment.Trials, neatOptions.NumRuns), RandSeed: seed, @@ -92,18 +92,21 @@ func main() { var generationEvaluator experiment.GenerationEvaluator switch *experimentName { case "XOR": - expt.MaxFitnessScore = 16.0 // as given by fitness function definition + exp.MaxFitnessScore = 16.0 // as given by fitness function definition generationEvaluator = xor.NewXORGenerationEvaluator(outDir) case "cart_pole": - expt.MaxFitnessScore = 1.0 // as given by fitness function definition - generationEvaluator = pole.NewCartPoleGenerationEvaluator(outDir, true, 500000) + exp.MaxFitnessScore = 1.0 // as given by fitness function definition + generationEvaluator = pole.NewCartPoleGenerationEvaluator(outDir, true, 1500000) + case "cart_pole_parallel": + exp.MaxFitnessScore = 1.0 // as given by fitness function definition + generationEvaluator = pole.NewCartPoleParallelGenerationEvaluator(outDir, true, 1500000) case "cart_2pole_markov": - expt.MaxFitnessScore = 1.0 // as given by fitness function definition + exp.MaxFitnessScore = 1.0 // as given by fitness function definition generationEvaluator = pole2.NewCartDoublePoleGenerationEvaluator(outDir, true, pole2.ContinuousAction) case "cart_2pole_non-markov": generationEvaluator = pole2.NewCartDoublePoleGenerationEvaluator(outDir, false, pole2.ContinuousAction) case "cart_2pole_markov_parallel": - expt.MaxFitnessScore = 1.0 // as given by fitness function definition + exp.MaxFitnessScore = 1.0 // as given by fitness function definition generationEvaluator = pole2.NewCartDoublePoleParallelGenerationEvaluator(outDir, true, pole2.ContinuousAction) default: log.Fatalf("Unsupported experiment: %s", *experimentName) @@ -115,7 +118,7 @@ func main() { // run experiment in the separate GO routine go func() { - if err = expt.Execute(neat.NewContext(ctx, neatOptions), startGenome, generationEvaluator, nil); err != nil { + if err = exp.Execute(neat.NewContext(ctx, neatOptions), startGenome, generationEvaluator, nil); err != nil { errChan <- err } else { errChan <- nil @@ -148,7 +151,7 @@ func main() { // Print experiment results statistics // - expt.PrintStatistics() + exp.PrintStatistics() fmt.Printf(">>> Start genome file: %s\n", *genomePath) fmt.Printf(">>> Configuration file: %s\n", *contextPath) @@ -158,7 +161,7 @@ func main() { expResPath := fmt.Sprintf("%s/%s.dat", outDir, *experimentName) if expResFile, err := os.Create(expResPath); err != nil { log.Fatal("Failed to create file for experiment results", err) - } else if err = expt.Write(expResFile); err != nil { + } else if err = exp.Write(expResFile); err != nil { log.Fatal("Failed to save experiment results", err) } @@ -167,7 +170,7 @@ func main() { npzResPath := fmt.Sprintf("%s/%s.npz", outDir, *experimentName) if npzResFile, err := os.Create(npzResPath); err != nil { log.Fatalf("Failed to create file for experiment results: [%s], reason: %s", npzResPath, err) - } else if err = expt.WriteNPZ(npzResFile); err != nil { + } else if err = exp.WriteNPZ(npzResFile); err != nil { log.Fatal("Failed to save experiment results as NPZ file", err) } }