github.com/yaricom/goNEAT@v0.0.0-20210507221059-e2110b885482/neat/genetics/genome_writer.go (about)

     1  package genetics
     2  
     3  import (
     4  	"io"
     5  	"bufio"
     6  	"fmt"
     7  	"gopkg.in/yaml.v2"
     8  	"github.com/spf13/cast"
     9  	"github.com/yaricom/goNEAT/neat"
    10  	"github.com/yaricom/goNEAT/neat/network"
    11  	"github.com/yaricom/goNEAT/neat/utils"
    12  )
    13  
    14  // The interface to define genome writer
    15  type GenomeWriter interface {
    16  	// Writes Genome record
    17  	WriteGenome(genome *Genome) error
    18  }
    19  
    20  // Creates genome writer with specified data encoding format
    21  func NewGenomeWriter(w io.Writer, encoding GenomeEncoding) (GenomeWriter, error) {
    22  	switch encoding {
    23  	case PlainGenomeEncoding:
    24  		return &plainGenomeWriter{w:bufio.NewWriter(w)}, nil
    25  	case YAMLGenomeEncoding:
    26  		return &yamlGenomeWriter{w:bufio.NewWriter(w)}, nil
    27  	default:
    28  		return nil, ErrUnsupportedGenomeEncoding
    29  	}
    30  }
    31  
    32  // The plain text encoded genome writer
    33  type plainGenomeWriter struct {
    34  	w *bufio.Writer
    35  }
    36  
    37  // Writes genome in Plain Text format
    38  func (wr *plainGenomeWriter) WriteGenome(g *Genome) error {
    39  	_, err := fmt.Fprintf(wr.w, "genomestart %d\n", g.Id)
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	for _, tr := range g.Traits {
    45  		fmt.Fprint(wr.w, "trait ")
    46  		err := wr.writeTrait(tr)
    47  		if err != nil {
    48  			return err
    49  		}
    50  		fmt.Fprintln(wr.w, "")
    51  	}
    52  
    53  	for _, nd := range g.Nodes {
    54  		fmt.Fprint(wr.w, "node ")
    55  		err := wr.writeNetworkNode(nd)
    56  		if err != nil {
    57  			return err
    58  		}
    59  		fmt.Fprintln(wr.w, "")
    60  	}
    61  
    62  	for _, gn := range g.Genes {
    63  		fmt.Fprint(wr.w, "gene ")
    64  		err := wr.writeConnectionGene(gn)
    65  		if err != nil {
    66  			return err
    67  		}
    68  		fmt.Fprintln(wr.w, "")
    69  	}
    70  	_, err = fmt.Fprintf(wr.w, "genomeend %d\n", g.Id)
    71  
    72  	// flush buffer
    73  	err = wr.w.Flush()
    74  
    75  	return err
    76  }
    77  
    78  // Dump trait in plain text format
    79  func (wr *plainGenomeWriter) writeTrait(t *neat.Trait) error {
    80  	_, err := fmt.Fprintf(wr.w, "%d ", t.Id)
    81  	if err == nil {
    82  		for i, p := range t.Params {
    83  			if i < len(t.Params) - 1 {
    84  				_, err = fmt.Fprintf(wr.w, "%g ", p)
    85  			} else {
    86  				_, err = fmt.Fprintf(wr.w, "%g", p)
    87  			}
    88  
    89  			if err != nil {
    90  				return err
    91  			}
    92  		}
    93  	}
    94  	return err
    95  }
    96  // Dump network node in plain text format
    97  func (wr *plainGenomeWriter) writeNetworkNode(n *network.NNode) error {
    98  	trait_id := 0
    99  	if n.Trait != nil {
   100  		trait_id = n.Trait.Id
   101  	}
   102  	act_str, err := utils.NodeActivators.ActivationNameFromType(n.ActivationType)
   103  	if err == nil {
   104  		_, err = fmt.Fprintf(wr.w, "%d %d %d %d %s", n.Id, trait_id, n.NodeType(),
   105  			n.NeuronType, act_str)
   106  	}
   107  	return err
   108  }
   109  // Dump connection gene in plain text format
   110  func (wr *plainGenomeWriter) writeConnectionGene(g *Gene) error {
   111  	link := g.Link
   112  	traitId := 0
   113  	if link.Trait != nil {
   114  		traitId = link.Trait.Id
   115  	}
   116  	inNodeId := link.InNode.Id
   117  	outNodeId := link.OutNode.Id
   118  	weight := link.Weight
   119  	recurrent := link.IsRecurrent
   120  	innov_num := g.InnovationNum
   121  	mut_num := g.MutationNum
   122  	enabled := g.IsEnabled
   123  
   124  	_, err := fmt.Fprintf(wr.w, "%d %d %d %g %t %d %g %t",
   125  		traitId, inNodeId, outNodeId, weight, recurrent, innov_num, mut_num, enabled)
   126  	return err
   127  }
   128  
   129  // The YAML encoded genome writer
   130  type yamlGenomeWriter struct {
   131  	w *bufio.Writer
   132  }
   133  
   134  func (wr *yamlGenomeWriter) WriteGenome(g *Genome) (err error) {
   135  	g_map := make(map[string]interface{})
   136  	g_map["id"] = g.Id
   137  
   138  	// encode traits
   139  	traits := make([]map[string]interface{}, len(g.Traits))
   140  	for i, t := range g.Traits {
   141  		traits[i] = wr.encodeGenomeTrait(t)
   142  	}
   143  	g_map["traits"] = traits
   144  
   145  	// encode network nodes
   146  	nodes := make([]map[string]interface{}, len(g.Nodes))
   147  	for i, n := range g.Nodes {
   148  		nodes[i], err = wr.encodeNetworkNode(n)
   149  		if err != nil {
   150  			return err
   151  		}
   152  	}
   153  	g_map["nodes"] = nodes
   154  
   155  	// encode connection genes
   156  	genes := make([]map[string]interface{}, len(g.Genes))
   157  	for i, gn := range g.Genes {
   158  		genes[i] = wr.encodeConnectionGene(gn)
   159  	}
   160  	g_map["genes"] = genes
   161  
   162  	// encode control genes if any
   163  	if len(g.ControlGenes) > 0 {
   164  		modules := make([]map[string]interface{}, len(g.ControlGenes))
   165  		for i, cg := range g.ControlGenes {
   166  			modules[i], err = wr.encodeControlGene(cg)
   167  			if err != nil {
   168  				return err
   169  			}
   170  		}
   171  		g_map["modules"] = modules
   172  	}
   173  
   174  
   175  	// store genome map
   176  	r_map := make(map[string]interface{})
   177  	r_map["genome"] = g_map
   178  
   179  	// encode everything as YAML
   180  	enc := yaml.NewEncoder(wr.w)
   181  	err = enc.Encode(r_map)
   182  	if err == nil {
   183  		// flush stream
   184  		err = wr.w.Flush()
   185  	}
   186  
   187  	return err
   188  }
   189  
   190  func (wr *yamlGenomeWriter) encodeControlGene(gene *MIMOControlGene) (g_map map[string]interface{}, err error) {
   191  	g_map = make(map[string]interface{})
   192  	g_map["id"] = gene.ControlNode.Id
   193  	if gene.ControlNode.Trait != nil {
   194  		g_map["trait_id"] = gene.ControlNode.Trait.Id
   195  	} else {
   196  		g_map["trait_id"] = 0
   197  	}
   198  	g_map["innov_num"] = gene.InnovationNum
   199  	g_map["mut_num"] = gene.MutationNum
   200  	g_map["enabled"] = gene.IsEnabled
   201  	g_map["activation"], err = utils.NodeActivators.ActivationNameFromType(gene.ControlNode.ActivationType)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	// store inputs
   206  	inputs := make([]map[string]interface{}, len(gene.ControlNode.Incoming))
   207  	for i, in := range gene.ControlNode.Incoming {
   208  		inputs[i] = wr.encodeModuleLink(in.InNode.Id, i)
   209  	}
   210  	g_map["inputs"] = inputs
   211  
   212  	// store outputs
   213  	outputs := make([]map[string]interface{}, len(gene.ControlNode.Outgoing))
   214  	for i, out := range gene.ControlNode.Outgoing {
   215  		outputs[i] = wr.encodeModuleLink(out.OutNode.Id, i)
   216  	}
   217  	g_map["outputs"] = outputs
   218  
   219  	return g_map, err
   220  }
   221  
   222  func (wr *yamlGenomeWriter) encodeModuleLink(id, order int) map[string]interface{} {
   223  	l_map := make(map[string]interface{})
   224  	l_map["id"] = id
   225  	l_map["order"] = order
   226  	return l_map
   227  }
   228  
   229  func (wr *yamlGenomeWriter) encodeConnectionGene(gene *Gene) map[string]interface{} {
   230  	g_map := make(map[string]interface{})
   231  	if gene.Link.Trait != nil {
   232  		g_map["trait_id"] = gene.Link.Trait.Id
   233  	} else {
   234  		g_map["trait_id"] = 0
   235  	}
   236  	g_map["src_id"] = gene.Link.InNode.Id
   237  	g_map["tgt_id"] = gene.Link.OutNode.Id
   238  	g_map["innov_num"] = gene.InnovationNum
   239  	g_map["weight"] = gene.Link.Weight
   240  	g_map["mut_num"] = gene.MutationNum
   241  	g_map["recurrent"] = cast.ToString(gene.Link.IsRecurrent)
   242  	g_map["enabled"] = cast.ToString(gene.IsEnabled)
   243  	return g_map
   244  }
   245  
   246  func (wr *yamlGenomeWriter) encodeNetworkNode(node *network.NNode) (n_map map[string]interface{}, err error) {
   247  	n_map = make(map[string]interface{})
   248  	n_map["id"] = node.Id
   249  	if node.Trait != nil {
   250  		n_map["trait_id"] = node.Trait.Id
   251  	} else {
   252  		n_map["trait_id"] = 0
   253  	}
   254  	n_map["type"] = network.NeuronTypeName(node.NeuronType)
   255  	n_map["activation"], err = utils.NodeActivators.ActivationNameFromType(node.ActivationType)
   256  	return n_map, err
   257  }
   258  
   259  func (wr *yamlGenomeWriter) encodeGenomeTrait(trait *neat.Trait) map[string]interface{} {
   260  	tr_map := make(map[string]interface{})
   261  	tr_map["id"] = trait.Id
   262  	tr_map["params"] = trait.Params
   263  	return tr_map
   264  }