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 }