github.com/yaricom/goNEAT@v0.0.0-20210507221059-e2110b885482/neat/genetics/genome_reader.go (about) 1 package genetics 2 3 import ( 4 "io" 5 "fmt" 6 "bufio" 7 "errors" 8 "strings" 9 "strconv" 10 "gopkg.in/yaml.v2" 11 "github.com/spf13/cast" 12 "github.com/yaricom/goNEAT/neat" 13 "github.com/yaricom/goNEAT/neat/utils" 14 "github.com/yaricom/goNEAT/neat/network" 15 ) 16 17 18 // The interface to define genome reader 19 type GenomeReader interface { 20 // Reads one Genome record 21 Read() (*Genome, error) 22 } 23 24 // Creates reader for Genome data with specified encoding format. 25 func NewGenomeReader(r io.Reader, encoding GenomeEncoding) (GenomeReader, error) { 26 switch encoding { 27 case PlainGenomeEncoding: 28 return &plainGenomeReader{r: bufio.NewReader(r)}, nil 29 case YAMLGenomeEncoding: 30 return &yamlGenomeReader{r: bufio.NewReader(r)}, nil 31 default: 32 return nil, ErrUnsupportedGenomeEncoding 33 } 34 } 35 36 // A PlainGenomeReader reads genome data from plain text file. 37 type plainGenomeReader struct { 38 r *bufio.Reader 39 } 40 41 func (pgr *plainGenomeReader) Read() (*Genome, error) { 42 gnome := Genome{ 43 Traits:make([]*neat.Trait, 0), 44 Nodes:make([]*network.NNode, 0), 45 Genes:make([]*Gene, 0), 46 } 47 48 var g_id int 49 // Loop until file is finished, parsing each line 50 scanner := bufio.NewScanner(pgr.r) 51 scanner.Split(bufio.ScanLines) 52 for scanner.Scan() { 53 line := scanner.Text() 54 parts := strings.SplitN(line, " ", 2) 55 if len(parts) < 2 { 56 return nil, errors.New(fmt.Sprintf("Line: [%s] can not be split when reading Genome", line)) 57 } 58 lr := strings.NewReader(parts[1]) 59 60 switch parts[0] { 61 case "trait": 62 // Read a Trait 63 new_trait, err := readPlainTrait(lr) 64 if err != nil { 65 return nil, err 66 } 67 // check that trait ID is unique 68 if prev_trait := traitWithId(new_trait.Id, gnome.Traits); prev_trait != nil { 69 return nil, errors.New( 70 fmt.Sprintf("Trait ID: %d is not unique", new_trait.Id)) 71 } 72 gnome.Traits = append(gnome.Traits, new_trait) 73 74 case "node": 75 // Read a Network Node 76 new_node, err := readPlainNetworkNode(lr, gnome.Traits) 77 if err != nil { 78 return nil, err 79 } 80 // check that node ID is unique 81 if prev_node := nodeWithId(new_node.Id, gnome.Nodes); prev_node != nil { 82 return nil, errors.New( 83 fmt.Sprintf("Node ID: %d is not unique", new_node.Id)) 84 } 85 gnome.Nodes = append(gnome.Nodes, new_node) 86 87 case "gene": 88 // Read a Gene 89 new_gene, err := readPlainConnectionGene(lr, gnome.Traits, gnome.Nodes) 90 if err != nil { 91 return nil, err 92 } 93 gnome.Genes = append(gnome.Genes, new_gene) 94 95 case "genomeend": 96 // Read Genome ID 97 _, err := fmt.Fscanf(lr, "%d", &g_id) 98 if err != nil { 99 return nil, err 100 } 101 // save genome ID 102 gnome.Id = g_id 103 104 case "/*": 105 // read all comments and print it 106 neat.InfoLog(line) 107 } 108 } 109 return &gnome, nil 110 } 111 112 // The method to read Trait in plain text format 113 func readPlainTrait(r io.Reader) (*neat.Trait, error) { 114 nt := neat.NewTrait() 115 _, err := fmt.Fscanf(r, "%d ", &nt.Id) 116 if err == nil { 117 for i := 0; i < neat.Num_trait_params; i++ { 118 if _, err = fmt.Fscanf(r, "%g ", &nt.Params[i]); err != nil { 119 return nil, err 120 } 121 } 122 } 123 124 return nt, err 125 } 126 127 // Read a Network Node from specified Reader in plain text format 128 // and applies corresponding trait to it from a list of traits provided 129 func readPlainNetworkNode(r io.Reader, traits []*neat.Trait) (*network.NNode, error) { 130 n := network.NewNetworkNode() 131 buf_r := bufio.NewReader(r) 132 line, _, err := buf_r.ReadLine() 133 if err != nil { 134 return nil, err 135 } 136 parts := strings.Split(string(line), " ") 137 if len(parts) < 4 { 138 return nil, errors.New(fmt.Sprintf("node line is too short: %d (%s)", len(parts), parts)) 139 } 140 if n_Id, err := strconv.ParseInt(parts[0], 10, 32); err != nil { 141 return nil, err 142 } else { 143 n.Id = int(n_Id) 144 } 145 if trait_id, err := strconv.ParseInt(parts[1], 10, 32); err != nil { 146 return nil, err 147 } else { 148 n.Trait = traitWithId(int(trait_id), traits) 149 } 150 151 if n_NeuronType, err := strconv.ParseInt(parts[3], 10, 32); err != nil { 152 return nil, err 153 } else { 154 n.NeuronType = network.NodeNeuronType(n_NeuronType) 155 } 156 157 if len(parts) == 5 { 158 n.ActivationType, err = utils.NodeActivators.ActivationTypeFromName(parts[4]) 159 } 160 161 return n, err 162 } 163 164 // Reads Gene from reader in plain text format 165 func readPlainConnectionGene(r io.Reader, traits []*neat.Trait, nodes []*network.NNode) (*Gene, error) { 166 var traitId, inNodeId, outNodeId int 167 var inov_num int64 168 var weight, mut_num float64 169 var recurrent, enabled bool 170 _, err := fmt.Fscanf(r, "%d %d %d %g %t %d %g %t ", 171 &traitId, &inNodeId, &outNodeId, &weight, &recurrent, &inov_num, &mut_num, &enabled) 172 if err != nil { 173 return nil, err 174 } 175 176 trait := traitWithId(traitId, traits) 177 var inNode, outNode *network.NNode 178 for _, np := range nodes { 179 if np.Id == inNodeId { 180 inNode = np 181 } 182 if np.Id == outNodeId { 183 outNode = np 184 } 185 } 186 if trait != nil { 187 return newGene(network.NewLinkWithTrait(trait, weight, inNode, outNode, recurrent), inov_num, mut_num, enabled), nil 188 } else { 189 return newGene(network.NewLink(weight, inNode, outNode, recurrent), inov_num, mut_num, enabled), nil 190 } 191 } 192 193 // A YAMLGenomeReader reads genome data from YAML encoded text file 194 type yamlGenomeReader struct { 195 r *bufio.Reader 196 } 197 198 func (ygr *yamlGenomeReader) Read() (*Genome, error) { 199 m := make(map[interface{}]interface{}) 200 dec := yaml.NewDecoder(ygr.r) 201 err := dec.Decode(&m) 202 if err != nil { 203 return nil, err 204 } 205 206 gm, ok := m["genome"].(map[interface{}]interface{}) 207 if ok == false { 208 return nil, errors.New("failed to parse YAML configuration") 209 } 210 211 // read Genome 212 gen_id, err := cast.ToIntE(gm["id"]) 213 if err != nil { 214 return nil, err 215 } 216 gnome := &Genome{ 217 Id:gen_id, 218 Traits:make([]*neat.Trait, 0), 219 Nodes:make([]*network.NNode, 0), 220 Genes:make([]*Gene, 0), 221 ControlGenes:make([]*MIMOControlGene, 0), 222 } 223 224 // read traits 225 traits := gm["traits"].([]interface{}) 226 for _, tr := range traits { 227 trait, err := readTrait(tr.(map[interface{}]interface{})) 228 if err != nil { 229 return nil, err 230 } 231 // check that trait ID is unique 232 if prev_trait := traitWithId(trait.Id, gnome.Traits); prev_trait != nil { 233 return nil, errors.New( 234 fmt.Sprintf("Trait ID: %d is not unique", trait.Id)) 235 } 236 gnome.Traits = append(gnome.Traits, trait) 237 } 238 239 // read nodes 240 nodes := gm["nodes"].([]interface{}) 241 for _, nd := range nodes { 242 node, err := readNNode(nd.(map[interface{}]interface{}), gnome.Traits) 243 if err != nil { 244 return nil, err 245 } 246 // check that node ID is unique 247 if prev_node := nodeWithId(node.Id, gnome.Nodes); prev_node != nil { 248 return nil, errors.New( 249 fmt.Sprintf("Node ID: %d is not unique", node.Id)) 250 } 251 gnome.Nodes = append(gnome.Nodes, node) 252 } 253 254 // read Genes 255 genes := gm["genes"].([]interface{}) 256 for _, g := range genes { 257 gene, err := readGene(g.(map[interface{}]interface{}), gnome.Traits, gnome.Nodes) 258 if err != nil { 259 return nil, err 260 } 261 gnome.Genes = append(gnome.Genes, gene) 262 } 263 264 // read MIMO control genes 265 mimoGenes := gm["modules"] 266 if mimoGenes != nil { 267 for _, mg := range mimoGenes.([]interface{}) { 268 mGene, err := readMIMOControlGene(mg.(map[interface{}]interface{}), gnome.Traits, gnome.Nodes) 269 if err != nil { 270 return nil, err 271 } 272 // check that control node ID is unique 273 if prev_node := nodeWithId(mGene.ControlNode.Id, gnome.Nodes); prev_node != nil { 274 return nil, errors.New( 275 fmt.Sprintf("Control node ID: %d is not unique", mGene.ControlNode.Id)) 276 } 277 gnome.ControlGenes = append(gnome.ControlGenes, mGene) 278 } 279 } 280 281 return gnome, nil 282 } 283 284 // Reads gene configuration 285 func readGene(conf map[interface{}]interface{}, traits []*neat.Trait, nodes []*network.NNode) (*Gene, error) { 286 traitId := conf["trait_id"].(int) 287 inNodeId := conf["src_id"].(int) 288 outNodeId := conf["tgt_id"].(int) 289 inov_num, err := cast.ToInt64E(conf["innov_num"]) 290 if err != nil { 291 return nil, err 292 } 293 weight, err := cast.ToFloat64E(conf["weight"]) 294 if err != nil { 295 return nil, err 296 } 297 mut_num, err := cast.ToFloat64E(conf["mut_num"]) 298 if err != nil { 299 return nil, err 300 } 301 recurrent, err := cast.ToBoolE(conf["recurrent"]) 302 if err != nil { 303 return nil, err 304 } 305 enabled, err := cast.ToBoolE(conf["enabled"]) 306 if err != nil { 307 return nil, err 308 } 309 310 trait := traitWithId(traitId, traits) 311 var inNode, outNode *network.NNode 312 for _, np := range nodes { 313 if np.Id == inNodeId { 314 inNode = np 315 } 316 if np.Id == outNodeId { 317 outNode = np 318 } 319 } 320 if trait != nil { 321 return newGene(network.NewLinkWithTrait(trait, weight, inNode, outNode, recurrent), inov_num, mut_num, enabled), nil 322 } else { 323 return newGene(network.NewLink(weight, inNode, outNode, recurrent), inov_num, mut_num, enabled), nil 324 } 325 } 326 327 // Reads MIMOControlGene configuration 328 func readMIMOControlGene(conf map[interface{}]interface{}, traits []*neat.Trait, nodes []*network.NNode) (gene *MIMOControlGene, err error) { 329 // read control node parameters 330 control_node := network.NewNetworkNode() 331 control_node.Id = conf["id"].(int) 332 control_node.NeuronType = network.HiddenNeuron 333 // set activation function 334 activation := conf["activation"].(string) 335 control_node.ActivationType, err = utils.NodeActivators.ActivationTypeFromName(activation) 336 if err != nil { 337 return nil, err 338 } 339 // set associated Trait 340 traitId := conf["trait_id"].(int) 341 trait := traitWithId(traitId, traits) 342 control_node.Trait = trait 343 344 // read MIMO gene parameters 345 inov_num, err := cast.ToInt64E(conf["innov_num"]) 346 if err != nil { 347 return nil, err 348 } 349 mut_num, err := cast.ToFloat64E(conf["mut_num"]) 350 if err != nil { 351 return nil, err 352 } 353 enabled, err := cast.ToBoolE(conf["enabled"]) 354 if err != nil { 355 return nil, err 356 } 357 358 // read input links 359 inNodes, err := cast.ToSliceE(conf["inputs"]) 360 if err != nil { 361 return nil, err 362 } 363 control_node.Incoming = make([]*network.Link, len(inNodes)) 364 for i, mn := range inNodes { 365 n := mn.(map[interface{}]interface{}) 366 node_id, err := cast.ToIntE(n["id"]) 367 if err != nil { 368 return nil, err 369 } 370 node := nodeWithId(node_id, nodes) 371 if node != nil { 372 control_node.Incoming[i] = network.NewLink(1.0, node, control_node, false) 373 } else { 374 return nil, errors.New(fmt.Sprintf("no MIMO input node with id: %d can be found for module: %d", 375 node_id, control_node.Id)) 376 } 377 } 378 379 // read output links 380 outNodes, err := cast.ToSliceE(conf["outputs"]) 381 if err != nil { 382 return nil, err 383 } 384 control_node.Outgoing = make([]*network.Link, len(outNodes)) 385 for i, mn := range outNodes { 386 n := mn.(map[interface{}]interface{}) 387 node_id, err := cast.ToIntE(n["id"]) 388 if err != nil { 389 return nil, err 390 } 391 node := nodeWithId(node_id, nodes) 392 if node != nil { 393 control_node.Outgoing[i] = network.NewLink(1.0, control_node, node, false) 394 } else { 395 return nil, errors.New(fmt.Sprintf("no MIMO output node with id: %d can be found for module: %d", 396 node_id, control_node.Id)) 397 } 398 } 399 400 // build gene 401 gene = NewMIMOGene(control_node, inov_num, mut_num, enabled) 402 403 return gene, nil 404 } 405 406 // Reads NNode configuration 407 func readNNode(conf map[interface{}]interface{}, traits []*neat.Trait) (*network.NNode, error) { 408 nd := network.NewNetworkNode() 409 nd.Id = conf["id"].(int) 410 trait_id := conf["trait_id"].(int) 411 nd.Trait = traitWithId(trait_id, traits) 412 type_name := conf["type"].(string) 413 var err error 414 nd.NeuronType, err = network.NeuronTypeByName(type_name) 415 if err != nil { 416 return nil, err 417 } 418 activation := conf["activation"].(string) 419 nd.ActivationType, err = utils.NodeActivators.ActivationTypeFromName(activation) 420 return nd, err 421 } 422 423 // Reads Trait configuration 424 func readTrait(conf map[interface{}]interface{}) (*neat.Trait, error) { 425 nt := neat.NewTrait() 426 nt.Id = conf["id"].(int) 427 params_c := cast.ToSlice(conf["params"]) 428 var err error 429 for i, p := range params_c { 430 nt.Params[i], err = cast.ToFloat64E(p) 431 if err != nil { 432 return nil, err 433 } 434 } 435 return nt, nil 436 } 437