github.com/yaricom/goNEAT@v0.0.0-20210507221059-e2110b885482/experiments/generation.go (about)

     1  package experiments
     2  
     3  import (
     4  	"time"
     5  	"github.com/yaricom/goNEAT/neat/genetics"
     6  	"math"
     7  	"encoding/gob"
     8  	"bytes"
     9  	"reflect"
    10  	"sort"
    11  )
    12  
    13  // The structure to represent execution results of one generation
    14  type Generation struct {
    15  	// The generation ID for this epoch
    16  	Id          int
    17  	// The time when epoch was evaluated
    18  	Executed    time.Time
    19  	// The elapsed time between generation execution start and finish
    20  	Duration    time.Duration
    21  	// The best organism of best species
    22  	Best        *genetics.Organism
    23  	// The flag to indicate whether experiment was solved in this epoch
    24  	Solved      bool
    25  
    26  	// The list of organisms fitness values per species in population
    27  	Fitness     Floats
    28  	// The age of organisms per species in population
    29  	Age         Floats
    30  	// The list of organisms complexities per species in population
    31  	Compexity   Floats
    32  
    33  	// The number of species in population at the end of this epoch
    34  	Diversity   int
    35  
    36  	// The number of evaluations done before winner found
    37  	WinnerEvals int
    38  	// The number of nodes in winner genome or zero if not solved
    39  	WinnerNodes int
    40  	// The numbers of genes (links) in winner genome or zero if not solved
    41  	WinnerGenes int
    42  
    43  	// The ID of Trial this Generation was evaluated in
    44  	TrialId     int
    45  }
    46  
    47  // Collects statistics about given population
    48  func (epoch *Generation) FillPopulationStatistics(pop *genetics.Population) {
    49  	max_fitness := float64(math.MinInt64)
    50  	epoch.Diversity = len(pop.Species)
    51  	epoch.Age = make(Floats, epoch.Diversity)
    52  	epoch.Compexity = make(Floats, epoch.Diversity)
    53  	epoch.Fitness = make(Floats, epoch.Diversity)
    54  	for i, curr_species := range pop.Species {
    55  		epoch.Age[i] = float64(curr_species.Age)
    56  		epoch.Compexity[i] = float64(curr_species.Organisms[0].Phenotype.Complexity())
    57  		epoch.Fitness[i] = curr_species.Organisms[0].Fitness
    58  
    59  		// find best organism in epoch if not solved
    60  		if !epoch.Solved {
    61  			// sort organisms from current species by fitness to have most fit first
    62  			sort.Sort(sort.Reverse(curr_species.Organisms))
    63  			if curr_species.Organisms[0].Fitness > max_fitness {
    64  				max_fitness = curr_species.Organisms[0].Fitness
    65  				epoch.Best = curr_species.Organisms[0]
    66  			}
    67  		}
    68  	}
    69  }
    70  
    71  // Returns average fitness, age, and complexity among all organisms from population at the end of this epoch
    72  func (epoch *Generation) Average() (fitness, age, complexity float64) {
    73  	fitness = epoch.Fitness.Mean()
    74  	age = epoch.Age.Mean()
    75  	complexity = epoch.Compexity.Mean()
    76  	return fitness, age, complexity
    77  }
    78  
    79  // Encodes generation with provided GOB encoder
    80  func (epoch *Generation) Encode(enc *gob.Encoder) error {
    81  	err := enc.EncodeValue(reflect.ValueOf(epoch.Id))
    82  	err = enc.EncodeValue(reflect.ValueOf(epoch.Executed))
    83  	err = enc.EncodeValue(reflect.ValueOf(epoch.Solved))
    84  	err = enc.EncodeValue(reflect.ValueOf(epoch.Fitness))
    85  	err = enc.EncodeValue(reflect.ValueOf(epoch.Age))
    86  	err = enc.EncodeValue(reflect.ValueOf(epoch.Compexity))
    87  	err = enc.EncodeValue(reflect.ValueOf(epoch.Diversity))
    88  	err = enc.EncodeValue(reflect.ValueOf(epoch.WinnerEvals))
    89  	err = enc.EncodeValue(reflect.ValueOf(epoch.WinnerNodes))
    90  	err = enc.EncodeValue(reflect.ValueOf(epoch.WinnerGenes))
    91  
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	// encode best organism
    97  	if epoch.Best != nil {
    98  		err = encodeOrganism(enc, epoch.Best)
    99  	}
   100  	return err
   101  }
   102  
   103  func encodeOrganism(enc *gob.Encoder, org *genetics.Organism) error {
   104  	err := enc.Encode(org.Fitness)
   105  	err = enc.Encode(org.IsWinner)
   106  	err = enc.Encode(org.Generation)
   107  	err = enc.Encode(org.ExpectedOffspring)
   108  	err = enc.Encode(org.Error)
   109  
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	// encode organism genome
   115  	if org.Genotype != nil {
   116  		err = enc.Encode(org.Genotype.Id)
   117  		out_buf := bytes.NewBufferString("")
   118  		org.Genotype.Write(out_buf)
   119  		err = enc.Encode(out_buf.Bytes())
   120  	}
   121  
   122  	return err
   123  }
   124  
   125  func (epoch *Generation) Decode(dec *gob.Decoder) error {
   126  	err := dec.Decode(&epoch.Id)
   127  	err = dec.Decode(&epoch.Executed)
   128  	err = dec.Decode(&epoch.Solved)
   129  	err = dec.Decode(&epoch.Fitness)
   130  	err = dec.Decode(&epoch.Age)
   131  	err = dec.Decode(&epoch.Compexity)
   132  	err = dec.Decode(&epoch.Diversity)
   133  	err = dec.Decode(&epoch.WinnerEvals)
   134  	err = dec.Decode(&epoch.WinnerNodes)
   135  	err = dec.Decode(&epoch.WinnerGenes)
   136  
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	// decode organism
   142  	org, err := decodeOrganism(dec)
   143  	if err == nil {
   144  		epoch.Best = org
   145  	}
   146  	return err
   147  }
   148  
   149  func decodeOrganism(dec *gob.Decoder) (*genetics.Organism, error) {
   150  	org := genetics.Organism{}
   151  	err := dec.Decode(&org.Fitness)
   152  	err = dec.Decode(&org.IsWinner)
   153  	err = dec.Decode(&org.Generation)
   154  	err = dec.Decode(&org.ExpectedOffspring)
   155  	err = dec.Decode(&org.Error)
   156  
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	// decode organism genome
   162  	var gen_id int
   163  	err = dec.Decode(&gen_id)
   164  	var data []byte
   165  	err = dec.Decode(&data)
   166  	gen, err := genetics.ReadGenome(bytes.NewBuffer(data), gen_id)
   167  	org.Genotype = gen
   168  
   169  	return &org, err
   170  }
   171  
   172  // Generations is a sortable collection of generations by execution time and Id
   173  type Generations []Generation
   174  
   175  func (is Generations) Len() int {
   176  	return len(is)
   177  }
   178  func (is Generations) Swap(i, j int) {
   179  	is[i], is[j] = is[j], is[i]
   180  }
   181  func (is Generations) Less(i, j int) bool {
   182  	if is[i].Executed.Equal(is[j].Executed) {
   183  		return is[i].Id < is[j].Id // less is from earlier epochs
   184  	}
   185  	return is[i].Executed.Before(is[j].Executed) // less is from earlier time
   186  }