Skip to content

Commit

Permalink
Merge pull request #42 from yaricom/experiment-utils
Browse files Browse the repository at this point in the history
Extracted common genome write functions used by examples into utils.
  • Loading branch information
yaricom committed Jan 9, 2022
2 parents 0e3dc0c + 0886c5a commit f0e8996
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 104 deletions.
35 changes: 11 additions & 24 deletions examples/pole/cart2pole.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package pole
import (
"fmt"
"github.com/yaricom/goNEAT/v2/experiment"
"github.com/yaricom/goNEAT/v2/experiment/utils"
"github.com/yaricom/goNEAT/v2/neat"
"github.com/yaricom/goNEAT/v2/neat/genetics"
"github.com/yaricom/goNEAT/v2/neat/network"
"github.com/yaricom/goNEAT/v2/neat/network/formats"
"math"
"os"
"sort"
)

Expand Down Expand Up @@ -220,10 +219,7 @@ func (e *cartDoublePoleGenerationEvaluator) GenerationEvaluate(pop *genetics.Pop

// Only print to file every print_every generation
if epoch.Solved || epoch.Id%context.PrintEvery == 0 {
popPath := fmt.Sprintf("%s/gen_%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId), epoch.Id)
if file, err := os.Create(popPath); err != nil {
return err
} else if err = pop.WriteBySpecies(file); err != nil {
if _, err = utils.WritePopulationPlain(e.OutputPath, pop, epoch); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump population, reason: %s\n", err))
return err
}
Expand All @@ -232,31 +228,22 @@ func (e *cartDoublePoleGenerationEvaluator) GenerationEvaluate(pop *genetics.Pop
if epoch.Solved {
// print winner organism
org := epoch.Best
depth, err := org.Phenotype.MaxActivationDepthFast(0) // The max depth of the network to be activated
if err == nil {
// The max depth of the network to be activated
if depth, err := org.Phenotype.MaxActivationDepthFast(0); err == nil {
neat.InfoLog(fmt.Sprintf("Activation depth of the winner: %d\n", depth))
}

genomeFile := "pole2_winner_genome"
// Prints the winner organism to file!
orgPath := fmt.Sprintf("%s/%s_%.1f_%d-%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"pole2_winner_genome", org.Fitness, org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return err
} else if err = org.Genotype.Write(file); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism genome, reason: %s\n", err))
return err
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 %d dumped to: %s\n", epoch.Id, org.Genotype.Id, orgPath))
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!
orgPath = fmt.Sprintf("%s/%s_%d-%d.cyjs", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"pole2_winner_phenome", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return err
} else if err = formats.WriteCytoscapeJSON(file, org.Phenotype); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism's phenome, reason: %s\n", err))
return err
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))
Expand All @@ -266,7 +253,7 @@ func (e *cartDoublePoleGenerationEvaluator) GenerationEvaluate(pop *genetics.Pop
return err
}

// This methods evaluates provided organism for cart double pole-balancing task
// orgEvaluate method evaluates provided organism for cart double pole-balancing task
func (e *cartDoublePoleGenerationEvaluator) orgEvaluate(organism *genetics.Organism, cartPole *CartPole) (winner bool, err error) {
// Try to balance a pole now
organism.Fitness, err = cartPole.evalNet(organism.Phenotype, e.ActionType)
Expand Down
39 changes: 10 additions & 29 deletions examples/pole/cartpole.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package pole
import (
"fmt"
"github.com/yaricom/goNEAT/v2/experiment"
"github.com/yaricom/goNEAT/v2/experiment/utils"
"github.com/yaricom/goNEAT/v2/neat"
"github.com/yaricom/goNEAT/v2/neat/genetics"
"github.com/yaricom/goNEAT/v2/neat/network"
"github.com/yaricom/goNEAT/v2/neat/network/formats"
"math"
"math/rand"
"os"
)

const twelveDegrees = 12.0 * math.Pi / 180.0
Expand Down Expand Up @@ -50,13 +49,8 @@ func (e *cartPoleGenerationEvaluator) GenerationEvaluate(pop *genetics.Populatio
epoch.Best = org
if epoch.WinnerNodes == 7 {
// You could dump out optimal genomes here if desired
optPath := fmt.Sprintf("%s/%s_%d-%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"pole1_optimal", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(optPath); err != nil {
return err
} else if err = org.Genotype.Write(file); err != nil {
if optPath, err := utils.WriteGenomePlain("pole1_optimal", e.OutputPath, org, epoch); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump optimal genome, reason: %s\n", err))
return err
} else {
neat.InfoLog(fmt.Sprintf("Dumped optimal genome to: %s\n", optPath))
}
Expand All @@ -69,10 +63,7 @@ func (e *cartPoleGenerationEvaluator) GenerationEvaluate(pop *genetics.Populatio

// Only print to file every print_every generation
if epoch.Solved || epoch.Id%context.PrintEvery == 0 {
popPath := fmt.Sprintf("%s/gen_%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId), epoch.Id)
if file, err := os.Create(popPath); err != nil {
return err
} else if err = pop.WriteBySpecies(file); err != nil {
if _, err = utils.WritePopulationPlain(e.OutputPath, pop, epoch); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump population, reason: %s\n", err))
return err
}
Expand All @@ -81,31 +72,21 @@ func (e *cartPoleGenerationEvaluator) GenerationEvaluate(pop *genetics.Populatio
if epoch.Solved {
// print winner organism
org := epoch.Best
depth, err := org.Phenotype.MaxActivationDepthFast(0)
if err == nil {
if depth, err := org.Phenotype.MaxActivationDepthFast(0); err == nil {
neat.InfoLog(fmt.Sprintf("Activation depth of the winner: %d\n", depth))
}

genomeFile := "pole1_winner_genome"
// Prints the winner organism to file!
orgPath := fmt.Sprintf("%s/%s_%d-%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"pole1_winner_genome", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return err
} else if err = org.Genotype.Write(file); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism genome, reason: %s\n", err))
return err
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 dumped to: %s\n", epoch.Id, orgPath))
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!
orgPath = fmt.Sprintf("%s/%s_%d-%d.cyjs", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"pole1_winner_phenome", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return err
} else if err = formats.WriteCytoscapeJSON(file, org.Phenotype); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism's phenome, reason: %s\n", err))
return err
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))
Expand Down
45 changes: 11 additions & 34 deletions examples/xor/XOR.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ package xor
import (
"fmt"
"github.com/yaricom/goNEAT/v2/experiment"
"github.com/yaricom/goNEAT/v2/experiment/utils"
"github.com/yaricom/goNEAT/v2/neat"
"github.com/yaricom/goNEAT/v2/neat/genetics"
"github.com/yaricom/goNEAT/v2/neat/network/formats"
"math"
"os"
)

// The fitness threshold value for successful solver
Expand Down Expand Up @@ -55,13 +54,8 @@ func (e *xorGenerationEvaluator) GenerationEvaluate(pop *genetics.Population, ep
epoch.Best = org
if epoch.WinnerNodes == 5 {
// You could dump out optimal genomes here if desired
optPath := fmt.Sprintf("%s/%s_%d-%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"xor_optimal", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(optPath); err != nil {
return err
} else if err = org.Genotype.Write(file); err != nil {
if optPath, err := utils.WriteGenomePlain("xor_optimal", e.OutputPath, org, epoch); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump optimal genome, reason: %s\n", err))
return err
} else {
neat.InfoLog(fmt.Sprintf("Dumped optimal genome to: %s\n", optPath))
}
Expand All @@ -74,10 +68,7 @@ func (e *xorGenerationEvaluator) GenerationEvaluate(pop *genetics.Population, ep

// Only print to file every print_every generation
if epoch.Solved || epoch.Id%context.PrintEvery == 0 {
popPath := fmt.Sprintf("%s/gen_%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId), epoch.Id)
if file, err := os.Create(popPath); err != nil {
return err
} else if err = pop.WriteBySpecies(file); err != nil {
if _, err = utils.WritePopulationPlain(e.OutputPath, pop, epoch); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump population, reason: %s\n", err))
return err
}
Expand All @@ -86,43 +77,29 @@ func (e *xorGenerationEvaluator) GenerationEvaluate(pop *genetics.Population, ep
if epoch.Solved {
// print winner organism
org := epoch.Best
depth, err := org.Phenotype.MaxActivationDepthFast(0)
if err == nil {
if depth, err := org.Phenotype.MaxActivationDepthFast(0); err == nil {
neat.InfoLog(fmt.Sprintf("Activation depth of the winner: %d\n", depth))
}

genomeFile := "xor_winner_genome"
// Prints the winner organism's Genome to the file!
orgPath := fmt.Sprintf("%s/%s_%d-%d", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"xor_winner_genome", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return err
} else if err = org.Genotype.Write(file); err != nil {
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))
return 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 DOT file!
orgPath = fmt.Sprintf("%s/%s_%d-%d.dot", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"xor_winner_phenome", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return err
} else if err = formats.WriteDOT(file, org.Phenotype); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism's phenome, reason: %s\n", err))
return err
if orgPath, err := utils.WriteGenomeDOT(genomeFile, e.OutputPath, org, epoch); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism's phenome DOT graph, reason: %s\n", err))
} else {
neat.InfoLog(fmt.Sprintf("Generation #%d winner's phenome DOT graph dumped to: %s\n",
epoch.Id, orgPath))
}

// Prints the winner organism's Phenotype to the Cytoscape JSON file!
orgPath = fmt.Sprintf("%s/%s_%d-%d.cyjs", experiment.OutDirForTrial(e.OutputPath, epoch.TrialId),
"xor_winner_phenome", org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return err
} else if err = formats.WriteCytoscapeJSON(file, org.Phenotype); err != nil {
neat.ErrorLog(fmt.Sprintf("Failed to dump winner organism's phenome, reason: %s\n", err))
return err
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))
Expand Down
17 changes: 0 additions & 17 deletions experiment/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ package experiment

import (
"errors"
"fmt"
"github.com/yaricom/goNEAT/v2/neat"
"github.com/yaricom/goNEAT/v2/neat/genetics"
"log"
"os"
)

// GenerationEvaluator the interface describing evaluator for one epoch (generation) of the evolutionary process.
Expand Down Expand Up @@ -38,17 +35,3 @@ func epochExecutorForContext(context *neat.Options) (genetics.PopulationEpochExe
return nil, errors.New("unsupported epoch executor type requested")
}
}

// OutDirForTrial To provide standard output directory syntax based on current trial
// Method checks if directory should be created
func OutDirForTrial(outDir string, trialID int) string {
dir := fmt.Sprintf("%s/%d", outDir, trialID)
if _, err := os.Stat(dir); err != nil {
// create output dir
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
log.Fatal("Failed to create output directory: ", err)
}
}
return dir
}
74 changes: 74 additions & 0 deletions experiment/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Package utils provides common utilities to be used by experiments.
package utils

import (
"fmt"
"github.com/yaricom/goNEAT/v2/experiment"
"github.com/yaricom/goNEAT/v2/neat/genetics"
"github.com/yaricom/goNEAT/v2/neat/network/formats"
"log"
"os"
)

// WriteGenomePlain is to write genome of the organism to the genomeFile in the outDir directory using plain encoding.
// The method return path to the file if successful or error if failed.
func WriteGenomePlain(genomeFile, outDir string, org *genetics.Organism, epoch *experiment.Generation) (string, error) {
orgPath := fmt.Sprintf("%s/%s_%d-%d", createOutDirForTrial(outDir, epoch.TrialId),
genomeFile, org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return "", err
} else if err = org.Genotype.Write(file); err != nil {
return "", err
}
return orgPath, nil
}

// WriteGenomeDOT is to write genome of the organism to the genomeFile in the outDir directory using DOT encoding.
// The method return path to the file if successful or error if failed.
func WriteGenomeDOT(genomeFile, outDir string, org *genetics.Organism, epoch *experiment.Generation) (string, error) {
orgPath := fmt.Sprintf("%s/%s_%d-%d.dot", createOutDirForTrial(outDir, epoch.TrialId),
genomeFile, org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return "", err
} else if err = formats.WriteDOT(file, org.Phenotype); err != nil {
return "", err
}
return orgPath, nil
}

// WriteGenomeCytoscapeJSON is to write genome of the organism to the genomeFile in the outDir directory using Cytoscape JSON encoding.
// The method return path to the file if successful or error if failed.
func WriteGenomeCytoscapeJSON(genomeFile, outDir string, org *genetics.Organism, epoch *experiment.Generation) (string, error) {
orgPath := fmt.Sprintf("%s/%s_%d-%d.cyjs", createOutDirForTrial(outDir, epoch.TrialId),
genomeFile, org.Phenotype.NodeCount(), org.Phenotype.LinkCount())
if file, err := os.Create(orgPath); err != nil {
return "", err
} else if err = formats.WriteCytoscapeJSON(file, org.Phenotype); err != nil {
return "", err
}
return orgPath, nil
}

// WritePopulationPlain is to write genomes of the entire population using plain encoding in the outDir directory.
// The methods return path to the file if successful or error if failed.
func WritePopulationPlain(outDir string, pop *genetics.Population, epoch *experiment.Generation) (string, error) {
popPath := fmt.Sprintf("%s/gen_%d", createOutDirForTrial(outDir, epoch.TrialId), epoch.Id)
if file, err := os.Create(popPath); err != nil {
return "", err
} else if err = pop.WriteBySpecies(file); err != nil {
return "", err
}
return popPath, nil
}

// createOutDirForTrial allows creating the output directory for specific trial using standard name.
func createOutDirForTrial(outDir string, trialID int) string {
dir := fmt.Sprintf("%s/%d", outDir, trialID)
if _, err := os.Stat(dir); err != nil {
// create output dir
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
log.Fatal("Failed to create output directory: ", err)
}
}
return dir
}

0 comments on commit f0e8996

Please sign in to comment.