From dc2606a2deb2fc546ccd696274bc734155a9cd80 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 21 Apr 2022 17:54:42 +0300 Subject: [PATCH 01/57] Updated .gitignore to exclude GoLand project file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 037cbdc..b57b01d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ vendor # The IDEA project forlder .idea +/goESHyperNEAT.iml # The MacOS specific .DS_Store @@ -22,3 +23,4 @@ vendor # Output of the go coverage tool, specifically when used with LiteIDE *.out + From a2c7f6107b5669823a527627fda2dab395d9be6a Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 21 Apr 2022 18:12:56 +0300 Subject: [PATCH 02/57] Updated eshyperneat library version to v2. Fixed comments and typos. --- cppn/cppn.go | 4 +-- cppn/evolvable_substrate.go | 10 +++--- cppn/evolvable_substrate_layout.go | 19 +++++----- cppn/evolvable_substrate_test.go | 2 +- cppn/quad_tree.go | 10 +++--- cppn/substrate.go | 2 +- cppn/substrate_graph_builder.go | 16 ++++----- cppn/substrate_layout.go | 16 ++++----- cppn/substrate_test.go | 2 +- eshyperneat/es_hyper_neat.go | 7 ++-- executor.go | 4 +-- experiments/retina/cppn_network_builder.go | 4 +-- experiments/retina/environment.go | 2 +- experiments/retina/retina.go | 2 +- go.mod | 24 +++++++++++-- go.sum | 40 ---------------------- hyperneat/hyper_neat.go | 4 +-- 17 files changed, 75 insertions(+), 93 deletions(-) diff --git a/cppn/cppn.go b/cppn/cppn.go index a199b2c..e671aa6 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -1,4 +1,4 @@ -// The package CPPN provides implementation of Compositional Pattern Producing Network +// Package cppn provides implementation of Compositional Pattern Producing Network // which is a part of Hypercube-based NEAT algorithm implementation package cppn @@ -10,7 +10,7 @@ import ( "os" ) -// Reads CPPN from specified genome and creates network solver +// ReadCPPFromGenomeFile Reads CPPN from specified genome and creates network solver func ReadCPPFromGenomeFile(genomePath string) (network.NetworkSolver, error) { if genomeFile, err := os.Open(genomePath); err != nil { return nil, err diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index ba25f20..fdb871a 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -2,7 +2,7 @@ package cppn import ( "container/list" - "github.com/yaricom/goESHyperNEAT/eshyperneat" + "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "math" "fmt" @@ -10,7 +10,7 @@ import ( "github.com/yaricom/goNEAT/neat/utils" ) -// The evolvable substrate holds configuration of ANN produced by CPPN within hypecube where each 4-dimensional point +// EvolvableSubstrate The evolvable substrate holds configuration of ANN produced by CPPN within hypecube where each 4-dimensional point // mark connection weight between two ANN units. The topology of ANN is not rigid as in plain substrate and can be evolved // by introducing novel nodes to the ANN. This provides extra benefits that the topology of ANN should not be handcrafted // by human, but produced during substrate generation from controlling CPPN and nodes locations may be arbitrary that suits @@ -27,7 +27,7 @@ type EvolvableSubstrate struct { coords []float64 } -// Creates new instance of evolvable substrate +// NewEvolvableSubstrate Creates new instance of evolvable substrate func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation utils.NodeActivationType) *EvolvableSubstrate { return &EvolvableSubstrate{ coords: make([]float64, 4), @@ -36,7 +36,7 @@ func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation util } } -// Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which +// CreateNetworkSolver Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which // used to define connections between network nodes. Optional graph_builder can be provided to collect graph nodes and edges // of created network solver. With graph builder it is possible to save/load network configuration as well as visualize it. func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.NetworkSolver, graphBuilder SubstrateGraphBuilder, context *eshyperneat.ESHyperNEATContext) (network.NetworkSolver, error) { @@ -274,7 +274,7 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, // Decides what regions should have higher neuron density based on variation and express new neurons and connections into // these regions. -// Receives coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b) and initialized quadtree node. +// Receive coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b) and initialized quadtree node. // Adds the connections that are in bands of the two-dimensional cross-section of the hypercube containing the source // or target node to the connections list and return modified list. func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadPoint, node *QuadNode, outgoing bool, context *eshyperneat.ESHyperNEATContext) ([]*QuadPoint, error) { diff --git a/cppn/evolvable_substrate_layout.go b/cppn/evolvable_substrate_layout.go index 2b62051..4aa5eea 100644 --- a/cppn/evolvable_substrate_layout.go +++ b/cppn/evolvable_substrate_layout.go @@ -7,26 +7,26 @@ import ( "github.com/yaricom/goNEAT/neat/network" ) -// Defines layout of neurons in the substrate +// EvolvableSubstrateLayout Defines layout of neurons in the substrate type EvolvableSubstrateLayout interface { - // Returns coordinates of the neuron with specified index [0; count) and type + // NodePosition Returns coordinates of the neuron with specified index [0; count) and type NodePosition(index int, nType network.NodeNeuronType) (*PointF, error) - // Adds new hidden node to the substrate + // AddHiddenNode Adds new hidden node to the substrate // Returns the index of added hidden neuron or error if failed. AddHiddenNode(position *PointF) (int, error) - // Returns index of hidden node at specified position or -1 if not fund + // IndexOfHidden Returns index of hidden node at specified position or -1 if not fund IndexOfHidden(position *PointF) int - // Returns number of INPUT neurons in the layout + // InputCount Returns number of INPUT neurons in the layout InputCount() int - // Returns number of HIDDEN neurons in the layout + // HiddenCount Returns number of HIDDEN neurons in the layout HiddenCount() int - // Returns number of OUTPUT neurons in the layout + // OutputCount Returns number of OUTPUT neurons in the layout OutputCount() int } -// Creates new instance with given input and output neurons count +// NewMappedEvolvableSubstrateLayout Creates new instance with given input and output neurons count func NewMappedEvolvableSubstrateLayout(inputCount, outputCount int) (*MappedEvolvableSubstrateLayout, error) { if inputCount == 0 { return nil, errors.New("the number of input neurons can not be ZERO") @@ -46,7 +46,8 @@ func NewMappedEvolvableSubstrateLayout(inputCount, outputCount int) (*MappedEvol return l, nil } -// The EvolvableSubstrateLayout implementation using map for binding between hidden node and its index +// MappedEvolvableSubstrateLayout The EvolvableSubstrateLayout implementation using map for binding between hidden +// node and its index type MappedEvolvableSubstrateLayout struct { // The map to hold binding between hidden node and its index for fast search hNodesMap map[PointF]int diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index 408f27d..c28e5e4 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -3,7 +3,7 @@ package cppn import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goESHyperNEAT/eshyperneat" + "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "github.com/yaricom/goNEAT/neat/utils" "os" "testing" diff --git a/cppn/quad_tree.go b/cppn/quad_tree.go index f78578e..db3c72a 100644 --- a/cppn/quad_tree.go +++ b/cppn/quad_tree.go @@ -2,7 +2,7 @@ package cppn import "fmt" -// Defines point with float precision coordinates +// PointF Defines point with float precision coordinates type PointF struct { X, Y float64 } @@ -15,7 +15,7 @@ func (p *PointF) String() string { return fmt.Sprintf("(%f, %f)", p.X, p.Y) } -// Defines the quad-point in the 4 dimensional hypercube +// QuadPoint Defines the quad-point in the 4 dimensional hypercube type QuadPoint struct { // The associated coordinates X1, X2, Y1, Y2 float64 @@ -27,12 +27,12 @@ func (q *QuadPoint) String() string { return fmt.Sprintf("((%f, %f),(%f, %f)) = %f", q.X1, q.Y1, q.X2, q.Y2, q.Value) } -// Creates new quad point +// NewQuadPoint Creates new quad point func NewQuadPoint(x1, y1, x2, y2, value float64) *QuadPoint { return &QuadPoint{X1: x1, Y1: y1, X2: x2, Y2: y2, Value: value} } -// Defines quad-tree node to model 4 dimensional hypercube +// QuadNode Defines quad-tree node to model 4 dimensional hypercube type QuadNode struct { // The coordinates of center of this quad-tree node's square X, Y float64 @@ -52,7 +52,7 @@ func (q *QuadNode) String() string { return fmt.Sprintf("((%f, %f), %f) = %f at %d", q.X, q.Y, q.Width, q.W, q.Level) } -// Creates new quad-node with given parameters +// NewQuadNode Creates new quad-node with given parameters func NewQuadNode(x, y, width float64, level int) *QuadNode { node := QuadNode{ X: x, diff --git a/cppn/substrate.go b/cppn/substrate.go index 64875e2..440e033 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -4,7 +4,7 @@ import ( "errors" "math" - "github.com/yaricom/goESHyperNEAT/hyperneat" + "github.com/yaricom/goESHyperNEAT/v2/hyperneat" "github.com/yaricom/goNEAT/neat/network" "github.com/yaricom/goNEAT/neat/utils" ) diff --git a/cppn/substrate_graph_builder.go b/cppn/substrate_graph_builder.go index e969922..fcf7aec 100644 --- a/cppn/substrate_graph_builder.go +++ b/cppn/substrate_graph_builder.go @@ -20,21 +20,21 @@ const ( edgeAttrTargetId = "targetId" ) -// The graph builder able to build weighted directed graphs representing substrate networks +// SubstrateGraphBuilder The graph builder able to build weighted directed graphs representing substrate networks type SubstrateGraphBuilder interface { - // Adds specified node to the graph with provided position + // AddNode Adds specified node to the graph with provided position AddNode(nodeId int, nodeNeuronType network.NodeNeuronType, nodeActivation utils.NodeActivationType, position *PointF) error - // Adds edge between two graph nodes + // AddWeightedEdge Adds edge between two graph nodes AddWeightedEdge(sourceId, targetId int, weight float64) error - // Returns the number of nodes in the graph + // NodesCount Returns the number of nodes in the graph NodesCount() (int, error) - // Returns the number of edges in the graph + // EdgesCount Returns the number of edges in the graph EdgesCount() (int, error) // Marshal graph to the provided writer Marshal(w io.Writer) error - // Unmarshal graph from the provided reader + // UnMarshal Unmarshal graph from the provided reader UnMarshal(r io.Reader) error } @@ -50,8 +50,8 @@ type graphMLBuilder struct { nodesMap map[int]*graphml.Node } -// Creates new instance with specified description to be included into serialized graph. If compact is true than graph -// will be marshaled into compact form +// NewSubstrateGraphMLBuilder Creates new instance with specified description to be included into serialized graph. +// If compact is true then graph will be marshaled into compact form func NewSubstrateGraphMLBuilder(description string, compact bool) SubstrateGraphBuilder { return &graphMLBuilder{ nodesMap: make(map[int]*graphml.Node), diff --git a/cppn/substrate_layout.go b/cppn/substrate_layout.go index 5925bc5..c7570a1 100644 --- a/cppn/substrate_layout.go +++ b/cppn/substrate_layout.go @@ -6,22 +6,22 @@ import ( "github.com/yaricom/goNEAT/neat/network" ) -// Defines layout of neurons in the substrate +// SubstrateLayout Defines layout of neurons in the substrate type SubstrateLayout interface { - // Returns coordinates of the neuron with specified index [0; count) and type + // NodePosition Returns coordinates of the neuron with specified index [0; count) and type NodePosition(index int, nType network.NodeNeuronType) (*PointF, error) - // Returns number of BIAS neurons in the layout + // BiasCount Returns number of BIAS neurons in the layout BiasCount() int - // Returns number of INPUT neurons in the layout + // InputCount Returns number of INPUT neurons in the layout InputCount() int - // Returns number of HIDDEN neurons in the layout + // HiddenCount Returns number of HIDDEN neurons in the layout HiddenCount() int - // Returns number of OUTPUT neurons in the layout + // OutputCount Returns number of OUTPUT neurons in the layout OutputCount() int } -// Defines grid substrate layout +// GridSubstrateLayout Defines grid substrate layout type GridSubstrateLayout struct { // The number of bias nodes encoded in this substrate biasCount int @@ -40,7 +40,7 @@ type GridSubstrateLayout struct { outputDelta float64 } -// Creates new instance with specified number of nodes to create layout for +// NewGridSubstrateLayout Creates new instance with specified number of nodes to create layout for func NewGridSubstrateLayout(biasCount, inputCount, outputCount, hiddenCount int) *GridSubstrateLayout { s := GridSubstrateLayout{biasCount: biasCount, inputCount: inputCount, outputCount: outputCount, hiddenCount: hiddenCount} diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index fb3de3f..56d5394 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -4,7 +4,7 @@ import ( "bytes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goESHyperNEAT/hyperneat" + "github.com/yaricom/goESHyperNEAT/v2/hyperneat" "github.com/yaricom/goNEAT/neat/utils" "os" "testing" diff --git a/eshyperneat/es_hyper_neat.go b/eshyperneat/es_hyper_neat.go index cc9c752..67c778f 100644 --- a/eshyperneat/es_hyper_neat.go +++ b/eshyperneat/es_hyper_neat.go @@ -1,14 +1,15 @@ +// Package eshyperneat holds implementation of Evolvable-Substrate HyperNEAT context package eshyperneat import ( "bytes" "errors" "github.com/spf13/viper" - "github.com/yaricom/goESHyperNEAT/hyperneat" + "github.com/yaricom/goESHyperNEAT/v2/hyperneat" "io" ) -// ES-HyperNEAT execution context +// ESHyperNEATContext ES-HyperNEAT execution context type ESHyperNEATContext struct { // The included HyperNEAT context *hyperneat.HyperNEATContext @@ -35,7 +36,7 @@ type ESHyperNEATContext struct { ESIterations int } -// Loads ESHyperNEAT context options from provided reader +// Load loads ESHyperNEAT context options from provided reader func Load(r io.Reader) (*ESHyperNEATContext, error) { var buff bytes.Buffer tee := io.TeeReader(r, &buff) diff --git a/executor.go b/executor.go index 7430d0a..854d764 100644 --- a/executor.go +++ b/executor.go @@ -9,8 +9,8 @@ import ( "strings" "time" - "github.com/yaricom/goESHyperNEAT/eshyperneat" - "github.com/yaricom/goESHyperNEAT/experiments/retina" + "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" + "github.com/yaricom/goESHyperNEAT/v2/experiments/retina" "github.com/yaricom/goNEAT/experiments" "github.com/yaricom/goNEAT/neat" "github.com/yaricom/goNEAT/neat/genetics" diff --git a/experiments/retina/cppn_network_builder.go b/experiments/retina/cppn_network_builder.go index 0ea7ecc..163b512 100644 --- a/experiments/retina/cppn_network_builder.go +++ b/experiments/retina/cppn_network_builder.go @@ -3,8 +3,8 @@ package retina import ( "fmt" - "github.com/yaricom/goESHyperNEAT/cppn" - "github.com/yaricom/goESHyperNEAT/eshyperneat" + "github.com/yaricom/goESHyperNEAT/v2/cppn" + "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "github.com/yaricom/goNEAT/neat/genetics" "github.com/yaricom/goNEAT/neat/network" "github.com/yaricom/goNEAT/neat/utils" diff --git a/experiments/retina/environment.go b/experiments/retina/environment.go index a3d1198..0c66c07 100644 --- a/experiments/retina/environment.go +++ b/experiments/retina/environment.go @@ -3,7 +3,7 @@ package retina import ( "fmt" "github.com/pkg/errors" - "github.com/yaricom/goESHyperNEAT/eshyperneat" + "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "strings" ) diff --git a/experiments/retina/retina.go b/experiments/retina/retina.go index 9f5e225..a14c2f0 100644 --- a/experiments/retina/retina.go +++ b/experiments/retina/retina.go @@ -4,7 +4,7 @@ package retina import ( "errors" "fmt" - "github.com/yaricom/goESHyperNEAT/cppn" + "github.com/yaricom/goESHyperNEAT/v2/cppn" "github.com/yaricom/goNEAT/experiments" "github.com/yaricom/goNEAT/neat" "github.com/yaricom/goNEAT/neat/genetics" diff --git a/go.mod b/go.mod index ee4cc4c..5cee050 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ -module github.com/yaricom/goESHyperNEAT +module github.com/yaricom/goESHyperNEAT/v2 -go 1.15 +go 1.17 require ( github.com/pkg/errors v0.9.1 @@ -9,3 +9,23 @@ require ( github.com/yaricom/goGraphML v1.1.0 github.com/yaricom/goNEAT v0.0.0-20210317183214-058a86c8043f ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.1 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/afero v1.1.2 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/jwalterweatherman v1.0.0 // indirect + github.com/spf13/pflag v1.0.3 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 // indirect + golang.org/x/text v0.3.2 // indirect + gopkg.in/ini.v1 v1.51.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/go.sum b/go.sum index 49a3122..80a934e 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,6 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -103,7 +101,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -112,8 +109,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= -github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -126,8 +121,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -135,14 +128,10 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -168,46 +157,28 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg= -github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/vdyagilev/goESHyperNEAT v0.0.0-20210320182635-8d87d8fe18a7 h1:mbACIIXEsYVqCO1K2AhYNRMVfdhu50tEMZG+SANTM/4= -github.com/vdyagilev/goESHyperNEAT v0.0.0-20210320182635-8d87d8fe18a7/go.mod h1:Tt2ks3HZwGkj5OOTviAYMHicWf62aqNKs6zprYEkr8c= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yaricom/goESHyperNEAT v0.0.0-20210315173628-f56070748916 h1:zSs84m58kO5SRbiW/xDhLgK6dveO/enp9Mb/IeM8jDk= -github.com/yaricom/goESHyperNEAT v0.0.0-20210315173628-f56070748916/go.mod h1:imok/JvhE9Y/9zBSo6gk6wB2eR/DMcsb+cUU07/haaY= -github.com/yaricom/goGraphML v0.0.0-20190109114128-b4665c578dfe h1:hZ3RTFDtNZpOHNKoyMY4yW6D00v44P0pXzhlG1MQdZw= -github.com/yaricom/goGraphML v0.0.0-20190109114128-b4665c578dfe/go.mod h1:brbnKEQs1Fw8GF0wuypX6DObHS9S/nGQv6jul8ZrWVI= github.com/yaricom/goGraphML v1.1.0 h1:CrM6yGmZ8Azv2Id2KIzei277MPe5YFzKkOOJu45uOBM= github.com/yaricom/goGraphML v1.1.0/go.mod h1:OM0MGAy6tdufwNYPW9BS2mR6NMArD7RtlakyTs+A3Vk= -github.com/yaricom/goNEAT v0.0.0-20190822164653-2553ade85ca4 h1:eUAy6gCm5s6bfGZTFZgYq7VcUwqdBchFAVQP39EcaYc= -github.com/yaricom/goNEAT v0.0.0-20190822164653-2553ade85ca4/go.mod h1:isxHmBMXtsc0Sl6KrQuU6VXFMx+un56M5FSwyLRXEB4= github.com/yaricom/goNEAT v0.0.0-20210317183214-058a86c8043f h1:PUjzjQbFUIiB8mO+VAH+Udg9zQqZmVbve15imziMwGg= github.com/yaricom/goNEAT v0.0.0-20210317183214-058a86c8043f/go.mod h1:GITpkyU810UM9mzboL5bpOuod7WWqehlsrY+xZ1VdlY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -221,7 +192,6 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -276,16 +246,10 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e h1:XNp2Flc/1eWQGk5BLzqTAN7fQIwIbfyVTuVxXxZh73M= -golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -334,13 +298,9 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index 55e6e5e..414e814 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -1,4 +1,4 @@ -// The package hyperneat holds implementation of HyperNEAT family of algorithms, including Evolvable-Substrate HyperNEAT +// Package hyperneat holds implementation of HyperNEAT family of algorithms package hyperneat import ( @@ -11,7 +11,7 @@ import ( "github.com/yaricom/goNEAT/neat/utils" ) -// The HyperNEAT execution context +// HyperNEATContext The HyperNEAT execution context type HyperNEATContext struct { // The NEAT context included *neat.NeatContext From 8256a318c50593856ce648cd3ddba8df9ba5473d Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 21 Apr 2022 18:21:50 +0300 Subject: [PATCH 03/57] Updated funding --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 5761136..ffcd629 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,4 @@ # These are supported funding model platforms patreon: io42 +custom: ["https://www.buymeacoffee.com/io42"] From ab67b0b5f8117e5013ba1dc36642283413949eaa Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 21 Apr 2022 18:44:25 +0300 Subject: [PATCH 04/57] Updated title of the README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b98171e..2fdd471 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# goESHyperNEAT + [![Build Status](https://travis-ci.org/yaricom/goESHyperNEAT.svg?branch=master)](https://travis-ci.org/yaricom/goESHyperNEAT) [![GoDoc](https://godoc.org/github.com/yaricom/goESHyperNEAT?status.svg)](https://godoc.org/github.com/yaricom/goESHyperNEAT) ## Overview From 6cd0db7d16f4a03ebbf3c92fddac837bb066086f Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 13:02:14 +0300 Subject: [PATCH 05/57] Updated release drafter configuration --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index fcca4ea..cc961a5 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,4 +1,4 @@ -name-template: 'v$RESOLVED_VERSION 🌈' +name-template: 'v$RESOLVED_VERSION 🇺🇦' tag-template: 'v$RESOLVED_VERSION' categories: - title: '🚀 Features' From cf93ed1a592912b4adf954f13dfbda2dfedce869 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 13:06:33 +0300 Subject: [PATCH 06/57] Updated README title --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fdd471..037b5e5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# goESHyperNEAT +# goESHyperNEAT 🇺🇦 [![Build Status](https://travis-ci.org/yaricom/goESHyperNEAT.svg?branch=master)](https://travis-ci.org/yaricom/goESHyperNEAT) [![GoDoc](https://godoc.org/github.com/yaricom/goESHyperNEAT?status.svg)](https://godoc.org/github.com/yaricom/goESHyperNEAT) From bdf2c8ac7b36cda9162e288ad5dcb0fb621872a0 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 13:11:20 +0300 Subject: [PATCH 07/57] Updated README. Added linter exclusion rule README.md --- .github/workflows/super-linter.yml | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 71acf06..42a14a1 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -26,4 +26,5 @@ jobs: env: VALIDATE_ALL_CODEBASE: false DEFAULT_BRANCH: master + FILTER_REGEX_EXCLUDE: .*README.md GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 037b5e5..1cd9a4f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ geometrical topology during training, producing regions with varying neural dens for situating cognitive structures in the biological brains. -## References: +## References 1. The original C++ NEAT implementation created by Kenneth O. Stanley, see: [NEAT][1] 2. Other NEAT implementations can be found at [NEAT Software Catalog][2] From f3fecb14c5f8bb1688cbb44d6b0bcbc04687787b Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 18:41:39 +0300 Subject: [PATCH 08/57] Updated CPPN implementation to use v2 of the goNEAT library. --- cppn/cppn.go | 34 +++--- cppn/evolvable_substrate.go | 26 +++-- cppn/evolvable_substrate_layout.go | 4 +- cppn/evolvable_substrate_layout_test.go | 2 +- cppn/evolvable_substrate_test.go | 11 +- cppn/substrate.go | 147 ++++++++++++------------ cppn/substrate_graph_builder.go | 18 +-- cppn/substrate_graph_builder_test.go | 22 ++-- cppn/substrate_layout.go | 2 +- cppn/substrate_layout_test.go | 2 +- cppn/substrate_test.go | 27 ++--- 11 files changed, 146 insertions(+), 149 deletions(-) diff --git a/cppn/cppn.go b/cppn/cppn.go index e671aa6..d971b8c 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -4,14 +4,14 @@ package cppn import ( "errors" - "github.com/yaricom/goNEAT/neat/genetics" - "github.com/yaricom/goNEAT/neat/network" + "github.com/yaricom/goNEAT/v2/neat/genetics" + "github.com/yaricom/goNEAT/v2/neat/network" "math" "os" ) // ReadCPPFromGenomeFile Reads CPPN from specified genome and creates network solver -func ReadCPPFromGenomeFile(genomePath string) (network.NetworkSolver, error) { +func ReadCPPFromGenomeFile(genomePath string) (network.Solver, error) { if genomeFile, err := os.Open(genomePath); err != nil { return nil, err } else if r, err := genetics.NewGenomeReader(genomeFile, genetics.YAMLGenomeEncoding); err != nil { @@ -33,9 +33,9 @@ func createThresholdNormalizedLink(cppnOutput float64, srcIndex, dstIndex int, l weight *= -1 // restore sign } link := network.FastNetworkLink{ - Weight: weight, - SourceIndx: srcIndex, - TargetIndx: dstIndex, + Weight: weight, + SourceIndex: srcIndex, + TargetIndex: dstIndex, } return &link } @@ -45,15 +45,15 @@ func createLink(cppnOutput float64, srcIndex, dstIndex int, weightRange float64) weight := cppnOutput weight *= weightRange // scale to fit given weight range link := network.FastNetworkLink{ - Weight: weight, - SourceIndx: srcIndex, - TargetIndx: dstIndex, + Weight: weight, + SourceIndex: srcIndex, + TargetIndex: dstIndex, } return &link } // Calculates outputs of provided CPPN network solver with given hypercube coordinates. -func queryCPPN(coordinates []float64, cppn network.NetworkSolver) ([]float64, error) { +func queryCPPN(coordinates []float64, cppn network.Solver) ([]float64, error) { // flush networks activation from previous run if res, err := cppn.Flush(); err != nil { return nil, err @@ -82,18 +82,18 @@ func nodeVariance(node *QuadNode) float64 { return 0.0 } - cppnVals := nodeCPPNValues(node) + cppnValues := nodeCPPNValues(node) // calculate median and variance m, v := 0.0, 0.0 - for _, f := range cppnVals { + for _, f := range cppnValues { m += f } - m /= float64(len(cppnVals)) + m /= float64(len(cppnValues)) - for _, f := range cppnVals { + for _, f := range cppnValues { v += math.Pow(f-m, 2) } - v /= float64(len(cppnVals)) + v /= float64(len(cppnValues)) return v } @@ -105,8 +105,8 @@ func nodeCPPNValues(n *QuadNode) []float64 { accumulator := make([]float64, 0) for _, p := range n.Nodes { // go into child nodes - pVals := nodeCPPNValues(p) - accumulator = append(accumulator, pVals...) + cppnValues := nodeCPPNValues(p) + accumulator = append(accumulator, cppnValues...) } return accumulator } else { diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index fdb871a..6fa6e14 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -6,8 +6,8 @@ import ( "math" "fmt" - "github.com/yaricom/goNEAT/neat/network" - "github.com/yaricom/goNEAT/neat/utils" + neatmath "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v2/neat/network" ) // EvolvableSubstrate The evolvable substrate holds configuration of ANN produced by CPPN within hypecube where each 4-dimensional point @@ -19,16 +19,16 @@ type EvolvableSubstrate struct { // The layout of neuron nodes in this substrate Layout EvolvableSubstrateLayout // The activation function's type for neurons encoded - NodesActivation utils.NodeActivationType + NodesActivation neatmath.NodeActivationType // The CPPN network solver to describe geometry of substrate - cppn network.NetworkSolver + cppn network.Solver // The reusable coordinates buffer coords []float64 } // NewEvolvableSubstrate Creates new instance of evolvable substrate -func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation utils.NodeActivationType) *EvolvableSubstrate { +func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation neatmath.NodeActivationType) *EvolvableSubstrate { return &EvolvableSubstrate{ coords: make([]float64, 4), Layout: layout, @@ -39,7 +39,8 @@ func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation util // CreateNetworkSolver Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which // used to define connections between network nodes. Optional graph_builder can be provided to collect graph nodes and edges // of created network solver. With graph builder it is possible to save/load network configuration as well as visualize it. -func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.NetworkSolver, graphBuilder SubstrateGraphBuilder, context *eshyperneat.ESHyperNEATContext) (network.NetworkSolver, error) { +func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuilder SubstrateGraphBuilder, + context *eshyperneat.Options) (network.Solver, error) { es.cppn = cppn // the network layers will be collected in order: bias, input, output, hidden @@ -73,7 +74,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.NetworkSolver, gr return nil, err } // add input node to graph - if _, err := addNodeToBuilder(graphBuilder, in, network.InputNeuron, utils.NullActivation, input); err != nil { + if _, err := addNodeToBuilder(graphBuilder, in, network.InputNeuron, neatmath.NullActivation, input); err != nil { return nil, err } @@ -207,11 +208,11 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.NetworkSolver, gr totalNeuronCount := es.Layout.InputCount() + es.Layout.OutputCount() + es.Layout.HiddenCount() // build activations - activations := make([]utils.NodeActivationType, totalNeuronCount) + activations := make([]neatmath.NodeActivationType, totalNeuronCount) for i := 0; i < totalNeuronCount; i++ { if i < firstOutput { // input nodes - NULL activation - activations[i] = utils.NullActivation + activations[i] = neatmath.NullActivation } else { // hidden/output nodes - defined activation activations[i] = es.NodesActivation @@ -229,7 +230,8 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.NetworkSolver, gr // Divides and initialize the quadtree from provided coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b). // Returns quadtree, in which each quadnode at (x,y) stores CPPN activation level for its position. The initialized // quadtree is used in the PruningAndExtraction phase to generate the actual ANN connections. -func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, context *eshyperneat.ESHyperNEATContext) (root *QuadNode, err error) { +func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, + context *eshyperneat.Options) (root *QuadNode, err error) { root = NewQuadNode(0.0, 0.0, 1.0, 1) queue := list.New() @@ -239,7 +241,7 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, // de-queue p := queue.Remove(queue.Front()).(*QuadNode) - // Divide into sub-regions and assign children to parent + // Divide into subregions and assign children to parent p.Nodes = []*QuadNode{ NewQuadNode(p.X-p.Width/2.0, p.Y-p.Width/2.0, p.Width/2.0, p.Level+1), NewQuadNode(p.X-p.Width/2.0, p.Y+p.Width/2.0, p.Width/2.0, p.Level+1), @@ -277,7 +279,7 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, // Receive coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b) and initialized quadtree node. // Adds the connections that are in bands of the two-dimensional cross-section of the hypercube containing the source // or target node to the connections list and return modified list. -func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadPoint, node *QuadNode, outgoing bool, context *eshyperneat.ESHyperNEATContext) ([]*QuadPoint, error) { +func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadPoint, node *QuadNode, outgoing bool, context *eshyperneat.Options) ([]*QuadPoint, error) { // fast check if len(node.Nodes) == 0 { return connections, nil diff --git a/cppn/evolvable_substrate_layout.go b/cppn/evolvable_substrate_layout.go index 4aa5eea..e2f2371 100644 --- a/cppn/evolvable_substrate_layout.go +++ b/cppn/evolvable_substrate_layout.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/pkg/errors" - "github.com/yaricom/goNEAT/neat/network" + "github.com/yaricom/goNEAT/v2/neat/network" ) // EvolvableSubstrateLayout Defines layout of neurons in the substrate @@ -46,7 +46,7 @@ func NewMappedEvolvableSubstrateLayout(inputCount, outputCount int) (*MappedEvol return l, nil } -// MappedEvolvableSubstrateLayout The EvolvableSubstrateLayout implementation using map for binding between hidden +// MappedEvolvableSubstrateLayout the EvolvableSubstrateLayout implementation using map for binding between hidden // node and its index type MappedEvolvableSubstrateLayout struct { // The map to hold binding between hidden node and its index for fast search diff --git a/cppn/evolvable_substrate_layout_test.go b/cppn/evolvable_substrate_layout_test.go index 80d3f79..cd96dd4 100644 --- a/cppn/evolvable_substrate_layout_test.go +++ b/cppn/evolvable_substrate_layout_test.go @@ -3,7 +3,7 @@ package cppn import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/neat/network" + "github.com/yaricom/goNEAT/v2/neat/network" "testing" ) diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index c28e5e4..b925d7c 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -4,8 +4,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goNEAT/neat/utils" - "os" + "github.com/yaricom/goNEAT/v2/neat/math" "testing" ) @@ -16,7 +15,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { layout, err := NewMappedEvolvableSubstrateLayout(inputCount, outputCount) require.NoError(t, err, "failed to create layout") - substr := NewEvolvableSubstrate(layout, utils.SigmoidSteepenedActivation) + substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation) cppn, err := ReadCPPFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") @@ -54,10 +53,8 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { } // Loads ES-HyperNeat context from provided config file's path -func loadESHyperNeatContext(configPath string) (*eshyperneat.ESHyperNEATContext, error) { - if r, err := os.Open(configPath); err != nil { - return nil, err - } else if ctx, err := eshyperneat.Load(r); err != nil { +func loadESHyperNeatContext(configPath string) (*eshyperneat.Options, error) { + if ctx, err := eshyperneat.LoadYAMLConfigFile(configPath); err != nil { return nil, err } else { return ctx, nil diff --git a/cppn/substrate.go b/cppn/substrate.go index 440e033..19f75e7 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -5,23 +5,23 @@ import ( "math" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" - "github.com/yaricom/goNEAT/neat/network" - "github.com/yaricom/goNEAT/neat/utils" + neatmath "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v2/neat/network" ) -// Represents substrate holding configuration of ANN with weights produced by CPPN. According to HyperNEAT method +// Substrate represents substrate holding configuration of ANN with weights produced by CPPN. According to HyperNEAT method // the ANN neurons are encoded as coordinates in hypercube presented by this substrate. -// By default neurons will be placed into substrate within grid layout +// By default, neurons will be placed into substrate within grid layout type Substrate struct { // The layout of neuron nodes in this substrate Layout SubstrateLayout // The activation function's type for neurons encoded - NodesActivation utils.NodeActivationType + NodesActivation neatmath.NodeActivationType } -// Creates new instance -func NewSubstrate(layout SubstrateLayout, nodesActivation utils.NodeActivationType) *Substrate { +// NewSubstrate creates new instance of substrate. +func NewSubstrate(layout SubstrateLayout, nodesActivation neatmath.NodeActivationType) *Substrate { substr := Substrate{ Layout: layout, NodesActivation: nodesActivation, @@ -29,13 +29,14 @@ func NewSubstrate(layout SubstrateLayout, nodesActivation utils.NodeActivationTy return &substr } -// Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which +// CreateNetworkSolver creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which // used to define connections between network nodes. Optional graph_builder can be provided to collect graph nodes and edges // of created network solver. With graph builder it is possible to save/load network configuration as well as visualize it. -// If use_leo is True thar Link Expression Output extension to the HyperNEAT will be used instead of standard weight threshold +// If the useLeo is True thar Link Expression Output extension to the HyperNEAT will be used instead of standard weight threshold // technique of HyperNEAT to determine whether to express link between two nodes or not. With LEO the link expressed based // on value of additional output of the CPPN (if > 0 then expressed) -func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool, graph_builder SubstrateGraphBuilder, context *hyperneat.HyperNEATContext) (network.NetworkSolver, error) { +func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphBuilder SubstrateGraphBuilder, + context *hyperneat.Options) (network.Solver, error) { // check conditions if s.Layout.BiasCount() > 1 { return nil, errors.New("SUBSTRATE: maximum one BIAS node per network supported") @@ -54,11 +55,11 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool biasList := make([]float64, totalNeuronCount) // inline function to find activation type for a given neuron - activationForNeuron := func(n_index int) utils.NodeActivationType { - if n_index < firstOutput { - // all bias and input neurons has null activation function associated because they actually has + activationForNeuron := func(nodeIndex int) neatmath.NodeActivationType { + if nodeIndex < firstOutput { + // all bias and input neurons has null activation function associated because they actually have // no inputs to be activated upon - return utils.NullActivation + return neatmath.NullActivation } else { return s.NodesActivation } @@ -66,18 +67,18 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool // give bias inputs to all hidden and output nodes. var link *network.FastNetworkLink - coord := make([]float64, 4) + coordinates := make([]float64, 4) for bi := firstBias; bi < firstInput; bi++ { // the bias coordinates - if b_coord, err := s.Layout.NodePosition(bi-firstBias, network.BiasNeuron); err != nil { + if biasPosition, err := s.Layout.NodePosition(bi-firstBias, network.BiasNeuron); err != nil { return nil, err } else { - coord[0] = b_coord.X - coord[1] = b_coord.Y + coordinates[0] = biasPosition.X + coordinates[1] = biasPosition.Y // add bias node to builder - if _, err := addNodeToBuilder(graph_builder, bi, network.BiasNeuron, activationForNeuron(bi), b_coord); err != nil { + if _, err = addNodeToBuilder(graphBuilder, bi, network.BiasNeuron, activationForNeuron(bi), biasPosition); err != nil { return nil, err } } @@ -85,32 +86,32 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool // link the bias to all hidden nodes. for hi := firstHidden; hi < lastHidden; hi++ { // get hidden neuron coordinates - if h_coord, err := s.Layout.NodePosition(hi-firstHidden, network.HiddenNeuron); err != nil { + if hiddenPosition, err := s.Layout.NodePosition(hi-firstHidden, network.HiddenNeuron); err != nil { return nil, err } else { - coord[2] = h_coord.X - coord[3] = h_coord.Y + coordinates[2] = hiddenPosition.X + coordinates[3] = hiddenPosition.Y // add node to graph - if _, err := addNodeToBuilder(graph_builder, hi, network.HiddenNeuron, activationForNeuron(hi), h_coord); err != nil { + if _, err = addNodeToBuilder(graphBuilder, hi, network.HiddenNeuron, activationForNeuron(hi), hiddenPosition); err != nil { return nil, err } } // find connection weight link = nil - if outs, err := queryCPPN(coord, cppn); err != nil { + if outs, err := queryCPPN(coordinates, cppn); err != nil { return nil, err - } else if use_leo && outs[1] > 0 { - // add links only when CPPN's LEO output signals to + } else if useLeo && outs[1] > 0 { + // add links only when CPPN LEO output signals to link = createLink(outs[0], bi, hi, context.WeightRange) - } else if !use_leo && math.Abs(outs[0]) > context.LinkThreshold { + } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { // add only connections with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], bi, hi, context.LinkThreshold, context.WeightRange) } if link != nil { biasList[hi] = link.Weight // add node and edge to graph - if _, err := addEdgeToBuilder(graph_builder, bi, hi, link.Weight); err != nil { + if _, err := addEdgeToBuilder(graphBuilder, bi, hi, link.Weight); err != nil { return nil, err } } @@ -119,32 +120,32 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool // link the bias to all output nodes for oi := firstOutput; oi < firstHidden; oi++ { // get output neuron coordinates - if o_coord, err := s.Layout.NodePosition(oi-firstOutput, network.OutputNeuron); err != nil { + if outputPosition, err := s.Layout.NodePosition(oi-firstOutput, network.OutputNeuron); err != nil { return nil, err } else { - coord[2] = o_coord.X - coord[3] = o_coord.Y + coordinates[2] = outputPosition.X + coordinates[3] = outputPosition.Y // add node to graph - if _, err := addNodeToBuilder(graph_builder, oi, network.OutputNeuron, activationForNeuron(oi), o_coord); err != nil { + if _, err = addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, activationForNeuron(oi), outputPosition); err != nil { return nil, err } } // find connection weight link = nil - if outs, err := queryCPPN(coord, cppn); err != nil { + if outs, err := queryCPPN(coordinates, cppn); err != nil { return nil, err - } else if use_leo && outs[1] > 0 { - // add links only when CPPN's LEO output signals to + } else if useLeo && outs[1] > 0 { + // add links only when CPPN LEO output signals to link = createLink(outs[0], bi, oi, context.WeightRange) - } else if !use_leo && math.Abs(outs[0]) > context.LinkThreshold { + } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { // add only connections with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], bi, oi, context.LinkThreshold, context.WeightRange) } if link != nil { biasList[oi] = link.Weight // add node and edge to graph - if _, err := addEdgeToBuilder(graph_builder, bi, oi, link.Weight); err != nil { + if _, err := addEdgeToBuilder(graphBuilder, bi, oi, link.Weight); err != nil { return nil, err } } @@ -155,33 +156,33 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool // link input nodes to hidden ones for in := firstInput; in < firstOutput; in++ { // get coordinates of input node - if i_coord, err := s.Layout.NodePosition(in-firstInput, network.InputNeuron); err != nil { + if inputPosition, err := s.Layout.NodePosition(in-firstInput, network.InputNeuron); err != nil { return nil, err } else { - coord[0] = i_coord.X - coord[1] = i_coord.Y + coordinates[0] = inputPosition.X + coordinates[1] = inputPosition.Y // add node to graph - if _, err := addNodeToBuilder(graph_builder, in, network.InputNeuron, activationForNeuron(in), i_coord); err != nil { + if _, err = addNodeToBuilder(graphBuilder, in, network.InputNeuron, activationForNeuron(in), inputPosition); err != nil { return nil, err } } for hi := firstHidden; hi < lastHidden; hi++ { // get hidden neuron coordinates - if h_coord, err := s.Layout.NodePosition(hi-firstHidden, network.HiddenNeuron); err != nil { + if hiddenPosition, err := s.Layout.NodePosition(hi-firstHidden, network.HiddenNeuron); err != nil { return nil, err } else { - coord[2] = h_coord.X - coord[3] = h_coord.Y + coordinates[2] = hiddenPosition.X + coordinates[3] = hiddenPosition.Y } // find connection weight link = nil - if outs, err := queryCPPN(coord, cppn); err != nil { + if outs, err := queryCPPN(coordinates, cppn); err != nil { return nil, err - } else if use_leo && outs[1] > 0 { - // add links only when CPPN's LEO output signals to + } else if useLeo && outs[1] > 0 { + // add links only when CPPN LEO output signals to link = createLink(outs[0], in, hi, context.WeightRange) - } else if !use_leo && math.Abs(outs[0]) > context.LinkThreshold { + } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { // add only connections with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], in, hi, context.LinkThreshold, context.WeightRange) @@ -189,7 +190,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool if link != nil { connections = append(connections, link) // add node and edge to graph - if _, err := addEdgeToBuilder(graph_builder, in, hi, link.Weight); err != nil { + if _, err := addEdgeToBuilder(graphBuilder, in, hi, link.Weight); err != nil { return nil, err } } @@ -198,35 +199,35 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool // link all hidden nodes to all output nodes. for hi := firstHidden; hi < lastHidden; hi++ { - if h_coord, err := s.Layout.NodePosition(hi-firstHidden, network.HiddenNeuron); err != nil { + if hiddenPosition, err := s.Layout.NodePosition(hi-firstHidden, network.HiddenNeuron); err != nil { return nil, err } else { - coord[0] = h_coord.X - coord[1] = h_coord.Y + coordinates[0] = hiddenPosition.X + coordinates[1] = hiddenPosition.Y } for oi := firstOutput; oi < firstHidden; oi++ { // get output neuron coordinates - if o_coord, err := s.Layout.NodePosition(oi-firstOutput, network.OutputNeuron); err != nil { + if outputPosition, err := s.Layout.NodePosition(oi-firstOutput, network.OutputNeuron); err != nil { return nil, err } else { - coord[2] = o_coord.X - coord[3] = o_coord.Y + coordinates[2] = outputPosition.X + coordinates[3] = outputPosition.Y } // find connection weight link = nil - if outs, err := queryCPPN(coord, cppn); err != nil { + if outs, err := queryCPPN(coordinates, cppn); err != nil { return nil, err - } else if use_leo && outs[1] > 0 { - // add links only when CPPN's LEO output signals to + } else if useLeo && outs[1] > 0 { + // add links only when CPPN LEO output signals to link = createLink(outs[0], hi, oi, context.WeightRange) - } else if !use_leo && math.Abs(outs[0]) > context.LinkThreshold { + } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { // add only connections with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], hi, oi, context.LinkThreshold, context.WeightRange) } if link != nil { connections = append(connections, link) // add node and edge to graph - if _, err := addEdgeToBuilder(graph_builder, hi, oi, link.Weight); err != nil { + if _, err := addEdgeToBuilder(graphBuilder, hi, oi, link.Weight); err != nil { return nil, err } } @@ -236,33 +237,33 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool // connect all input nodes directly to all output nodes for in := firstInput; in < firstOutput; in++ { // get coordinates of input node - if i_coord, err := s.Layout.NodePosition(in-firstInput, network.InputNeuron); err != nil { + if inputPosition, err := s.Layout.NodePosition(in-firstInput, network.InputNeuron); err != nil { return nil, err } else { - coord[0] = i_coord.X - coord[1] = i_coord.Y + coordinates[0] = inputPosition.X + coordinates[1] = inputPosition.Y // add node to graph - if _, err := addNodeToBuilder(graph_builder, in, network.InputNeuron, activationForNeuron(in), i_coord); err != nil { + if _, err = addNodeToBuilder(graphBuilder, in, network.InputNeuron, activationForNeuron(in), inputPosition); err != nil { return nil, err } } for oi := firstOutput; oi < firstHidden; oi++ { // get output neuron coordinates - if o_coord, err := s.Layout.NodePosition(oi-firstOutput, network.OutputNeuron); err != nil { + if outputPosition, err := s.Layout.NodePosition(oi-firstOutput, network.OutputNeuron); err != nil { return nil, err } else { - coord[2] = o_coord.X - coord[3] = o_coord.Y + coordinates[2] = outputPosition.X + coordinates[3] = outputPosition.Y } // find connection weight link = nil - if outs, err := queryCPPN(coord, cppn); err != nil { + if outs, err := queryCPPN(coordinates, cppn); err != nil { return nil, err - } else if use_leo && outs[1] > 0 { - // add links only when CPPN's LEO output signals to + } else if useLeo && outs[1] > 0 { + // add links only when CPPN LEO output signals to link = createLink(outs[0], in, oi, context.WeightRange) - } else if !use_leo && math.Abs(outs[0]) > context.LinkThreshold { + } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { // add only connections with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], in, oi, context.LinkThreshold, context.WeightRange) @@ -270,7 +271,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool if link != nil { connections = append(connections, link) // add node and edge to graph - if _, err := addEdgeToBuilder(graph_builder, in, oi, link.Weight); err != nil { + if _, err := addEdgeToBuilder(graphBuilder, in, oi, link.Weight); err != nil { return nil, err } } @@ -279,7 +280,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.NetworkSolver, use_leo bool } // build activations - activations := make([]utils.NodeActivationType, totalNeuronCount) + activations := make([]neatmath.NodeActivationType, totalNeuronCount) for i := 0; i < totalNeuronCount; i++ { activations[i] = activationForNeuron(i) } diff --git a/cppn/substrate_graph_builder.go b/cppn/substrate_graph_builder.go index fcf7aec..272c2f2 100644 --- a/cppn/substrate_graph_builder.go +++ b/cppn/substrate_graph_builder.go @@ -5,8 +5,8 @@ import ( "io" "github.com/yaricom/goGraphML/graphml" - "github.com/yaricom/goNEAT/neat/network" - "github.com/yaricom/goNEAT/neat/utils" + "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v2/neat/network" ) const ( @@ -23,7 +23,7 @@ const ( // SubstrateGraphBuilder The graph builder able to build weighted directed graphs representing substrate networks type SubstrateGraphBuilder interface { // AddNode Adds specified node to the graph with provided position - AddNode(nodeId int, nodeNeuronType network.NodeNeuronType, nodeActivation utils.NodeActivationType, position *PointF) error + AddNode(nodeId int, nodeNeuronType network.NodeNeuronType, nodeActivation math.NodeActivationType, position *PointF) error // AddWeightedEdge Adds edge between two graph nodes AddWeightedEdge(sourceId, targetId int, weight float64) error @@ -32,9 +32,9 @@ type SubstrateGraphBuilder interface { // EdgesCount Returns the number of edges in the graph EdgesCount() (int, error) - // Marshal graph to the provided writer + // Marshal the graph to the provided writer Marshal(w io.Writer) error - // UnMarshal Unmarshal graph from the provided reader + // UnMarshal is to unmarshal graph from the provided reader UnMarshal(r io.Reader) error } @@ -60,12 +60,13 @@ func NewSubstrateGraphMLBuilder(description string, compact bool) SubstrateGraph } } -func (b *graphMLBuilder) AddNode(nodeId int, nodeNeuronType network.NodeNeuronType, nodeActivation utils.NodeActivationType, position *PointF) (err error) { +func (b *graphMLBuilder) AddNode(nodeId int, nodeNeuronType network.NodeNeuronType, + nodeActivation math.NodeActivationType, position *PointF) (err error) { // create attributes map nodeAttr := make(map[string]interface{}) nodeAttr[nodeAttrID] = nodeId nodeAttr[nodeAttrNodeNeuronType] = network.NeuronTypeName(nodeNeuronType) - if nodeAttr[nodeAttrNodeActivationType], err = utils.NodeActivators.ActivationNameFromType(nodeActivation); err != nil { + if nodeAttr[nodeAttrNodeActivationType], err = math.NodeActivators.ActivationNameFromType(nodeActivation); err != nil { return err } nodeAttr[nodeAttrX] = position.X @@ -150,7 +151,8 @@ func (b *graphMLBuilder) graph() (*graphml.Graph, error) { return b.graphML.Graphs[0], nil } -func addNodeToBuilder(builder SubstrateGraphBuilder, nodeId int, nodeType network.NodeNeuronType, nodeActivation utils.NodeActivationType, position *PointF) (bool, error) { +func addNodeToBuilder(builder SubstrateGraphBuilder, nodeId int, nodeType network.NodeNeuronType, + nodeActivation math.NodeActivationType, position *PointF) (bool, error) { if builder == nil { return false, nil } else if err := builder.AddNode(nodeId, nodeType, nodeActivation, position); err != nil { diff --git a/cppn/substrate_graph_builder_test.go b/cppn/substrate_graph_builder_test.go index 29a7ed2..296c3bb 100644 --- a/cppn/substrate_graph_builder_test.go +++ b/cppn/substrate_graph_builder_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/yaricom/goNEAT/neat/network" - "github.com/yaricom/goNEAT/neat/utils" + "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v2/neat/network" ) func TestNewGraphMLBuilder(t *testing.T) { @@ -33,7 +33,7 @@ func TestGraphMLBuilder_AddNode(t *testing.T) { err := builder.AddNode( node[nodeAttrID].(int), node[nodeAttrNodeNeuronType].(network.NodeNeuronType), - node[nodeAttrNodeActivationType].(utils.NodeActivationType), + node[nodeAttrNodeActivationType].(math.NodeActivationType), position) require.NoError(t, err, "failed to add node") } @@ -53,7 +53,7 @@ func TestGraphMLBuilder_AddNode(t *testing.T) { nnt := network.NeuronTypeName(v.(network.NodeNeuronType)) assert.Equal(t, nnt, gnAttr[k], "wrong neuron type") } else if k == nodeAttrNodeActivationType { - nat, err := utils.NodeActivators.ActivationNameFromType(v.(utils.NodeActivationType)) + nat, err := math.NodeActivators.ActivationNameFromType(v.(math.NodeActivationType)) assert.NoError(t, err, "failed to get activation name from type: %v", v) assert.Equal(t, nat, gnAttr[k], "wrong activation type") } else if gnAttr[k] != v { @@ -74,7 +74,7 @@ func TestGraphMLBuilder_AddWeightedEdge(t *testing.T) { err := builder.AddNode( node[nodeAttrID].(int), node[nodeAttrNodeNeuronType].(network.NodeNeuronType), - node[nodeAttrNodeActivationType].(utils.NodeActivationType), + node[nodeAttrNodeActivationType].(math.NodeActivationType), position) require.NoError(t, err, "failed to add node: %v", node) } @@ -111,7 +111,7 @@ func TestGraphMLBuilder_Marshal(t *testing.T) { err := builder.AddNode( node[nodeAttrID].(int), node[nodeAttrNodeNeuronType].(network.NodeNeuronType), - node[nodeAttrNodeActivationType].(utils.NodeActivationType), + node[nodeAttrNodeActivationType].(math.NodeActivationType), position) require.NoError(t, err, "failed to add node: %v", node) } @@ -156,11 +156,11 @@ func createTestEdges() []map[string]interface{} { func createTestNodes() []map[string]interface{} { return []map[string]interface{}{ - {"id": 1, "X": -0.5, "Y": -1.0, "NodeNeuronType": network.InputNeuron, "NodeActivationType": utils.NullActivation}, - {"id": 2, "X": 0.5, "Y": -1.0, "NodeNeuronType": network.InputNeuron, "NodeActivationType": utils.NullActivation}, - {"id": 3, "X": 0.0, "Y": 0.0, "NodeNeuronType": network.HiddenNeuron, "NodeActivationType": utils.SigmoidSteepenedActivation}, - {"id": 4, "X": 0.0, "Y": 0.0, "NodeNeuronType": network.HiddenNeuron, "NodeActivationType": utils.SigmoidSteepenedActivation}, - {"id": 5, "X": 0.0, "Y": 1.0, "NodeNeuronType": network.OutputNeuron, "NodeActivationType": utils.LinearActivation}, + {"id": 1, "X": -0.5, "Y": -1.0, "NodeNeuronType": network.InputNeuron, "NodeActivationType": math.NullActivation}, + {"id": 2, "X": 0.5, "Y": -1.0, "NodeNeuronType": network.InputNeuron, "NodeActivationType": math.NullActivation}, + {"id": 3, "X": 0.0, "Y": 0.0, "NodeNeuronType": network.HiddenNeuron, "NodeActivationType": math.SigmoidSteepenedActivation}, + {"id": 4, "X": 0.0, "Y": 0.0, "NodeNeuronType": network.HiddenNeuron, "NodeActivationType": math.SigmoidSteepenedActivation}, + {"id": 5, "X": 0.0, "Y": 1.0, "NodeNeuronType": network.OutputNeuron, "NodeActivationType": math.LinearActivation}, } } diff --git a/cppn/substrate_layout.go b/cppn/substrate_layout.go index c7570a1..46d3aee 100644 --- a/cppn/substrate_layout.go +++ b/cppn/substrate_layout.go @@ -3,7 +3,7 @@ package cppn import ( "errors" "fmt" - "github.com/yaricom/goNEAT/neat/network" + "github.com/yaricom/goNEAT/v2/neat/network" ) // SubstrateLayout Defines layout of neurons in the substrate diff --git a/cppn/substrate_layout_test.go b/cppn/substrate_layout_test.go index ec5b904..1a7b772 100644 --- a/cppn/substrate_layout_test.go +++ b/cppn/substrate_layout_test.go @@ -2,7 +2,7 @@ package cppn import ( "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/neat/network" + "github.com/yaricom/goNEAT/v2/neat/network" "testing" ) diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 56d5394..cd2a6d2 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -5,8 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" - "github.com/yaricom/goNEAT/neat/utils" - "os" + "github.com/yaricom/goNEAT/v2/neat/math" "testing" ) @@ -17,8 +16,8 @@ func TestNewSubstrate(t *testing.T) { layout := NewGridSubstrateLayout(biasCount, inputCount, outputCount, hiddenCount) // create new substrate - substr := NewSubstrate(layout, utils.SigmoidSteepenedActivation) - assert.Equal(t, utils.SigmoidSteepenedActivation, substr.NodesActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) + assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) } func TestSubstrate_CreateNetworkSolver(t *testing.T) { @@ -26,8 +25,8 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { layout := NewGridSubstrateLayout(biasCount, inputCount, outputCount, hiddenCount) // create new substrate - substr := NewSubstrate(layout, utils.SigmoidSteepenedActivation) - assert.Equal(t, utils.SigmoidSteepenedActivation, substr.NodesActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) + assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) // create solver from substrate cppn, err := ReadCPPFromGenomeFile(cppnHyperNEATTestGenomePath) @@ -69,8 +68,8 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { layout := NewGridSubstrateLayout(biasCount, inputCount, outputCount, hiddenCount) // create new substrate - substr := NewSubstrate(layout, utils.SigmoidSteepenedActivation) - assert.Equal(t, utils.SigmoidSteepenedActivation, substr.NodesActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) + assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) // create solver from substrate cppn, err := ReadCPPFromGenomeFile(cppnLeoHyperNEATTestGenomePath) @@ -115,7 +114,7 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { builder := NewSubstrateGraphMLBuilder("", false).(*graphMLBuilder) // create new substrate - substr := NewSubstrate(layout, utils.SigmoidSteepenedActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) // create solver from substrate cppn, err := ReadCPPFromGenomeFile(cppnHyperNEATTestGenomePath) @@ -161,14 +160,10 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { } // Loads HyperNeat context from provided config file's path -func loadHyperNeatContext(configPath string) (*hyperneat.HyperNEATContext, error) { - if r, err := os.Open(configPath); err != nil { +func loadHyperNeatContext(configPath string) (*hyperneat.Options, error) { + if context, err := hyperneat.LoadYAMLConfigFile(configPath); err != nil { return nil, err } else { - if context, err := hyperneat.Load(r); err != nil { - return nil, err - } else { - return context, nil - } + return context, nil } } From fb8a0ec3a92b66ffc5527ace572200894a9f98a0 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 18:42:48 +0300 Subject: [PATCH 09/57] Updated ES-HyperNEAT and HyperNEAT implementation to use v2 of the goNEAT library. --- data/test/test_es_hyper.neat.yml | 144 +++++------------------------- data/test/test_hyper.neat.yml | 113 ++--------------------- eshyperneat/context.go | 23 +++++ eshyperneat/es_hyper_neat.go | 75 ++++++---------- eshyperneat/es_hyper_neat_test.go | 42 ++++++--- hyperneat/context.go | 23 +++++ hyperneat/hyper_neat.go | 76 +++++++--------- hyperneat/hyper_neat_test.go | 30 +++++-- 8 files changed, 184 insertions(+), 342 deletions(-) create mode 100644 eshyperneat/context.go create mode 100644 hyperneat/context.go diff --git a/data/test/test_es_hyper.neat.yml b/data/test/test_es_hyper.neat.yml index d923630..07c26af 100644 --- a/data/test/test_es_hyper.neat.yml +++ b/data/test/test_es_hyper.neat.yml @@ -1,132 +1,30 @@ -########################################## -# The common NEAT specific configuration # -########################################## -neat: - # Probability of mutating a single trait param - trait_param_mut_prob: 0.5 - # Power of mutation on a single trait param - trait_mutation_power: 1.0 - # The power of a link weight mutation - weight_mut_power: 2.5 - - # 3 global coefficients are used to determine the formula for computing the compatibility between 2 genomes. - # The formula is: disjoint_coeff * pdg + excess_coeff * peg + mutdiff_coeff * mdmg. - # See the Compatibility method in the Genome class for more info - # They can be thought of as the importance of disjoint Genes, excess Genes, and parametric difference between Genes of - # the same function, respectively. - disjoint_coeff: 1.0 - excess_coeff: 1.0 - mutdiff_coeff: 0.4 - - # This global tells compatibility threshold under which two Genomes are considered the same species - compat_threshold: 3.0 - # How much does age matter? Gives a fitness boost up to some young age (niching). If it is 1, then young species get no fitness boost. - age_significance: 1.0 - # Percent of average fitness for survival, how many get to reproduce based on survival_thresh * pop_size - survival_thresh: 0.2 - - # Probabilities of a non-mating reproduction - mutate_only_prob: 0.25 - # Probability of genome trait mutation - mutate_random_trait_prob: 0.1 - # Probability of link trait mutation - mutate_link_trait_prob: 0.1 - # Probability of node trait mutation - mutate_node_trait_prob: 0.1 - # Probability of link weight value mutation - mutate_link_weights_prob: 0.9 - # Probability of enabling/disabling of specific link/gene - mutate_toggle_enable_prob: 0.0 - # Probability of finding the first disabled gene and re-enabling it - mutate_gene_reenable_prob: 0.0 - # Probability of adding new node - mutate_add_node_prob: 0.03 - # Probability of adding new link between nodes - mutate_add_link_prob: 0.08 - # Probability of making connections from disconnected sensors (input, bias type neurons) - mutate_connect_sensors: 0.5 - - # Probability of mating between different species - interspecies_mate_rate: 0.001 - # Probability of mating this Genome with another Genome g. For every point in each Genome, where each Genome shares - # the innovation number, the Gene is chosen randomly from either parent. If one parent has an innovation absent in - # the other, the baby may inherit the innovation if it is from the more fit parent. - mate_multipoint_prob: 0.3 - # Probability of mating like in multipoint, but instead of selecting one or the other when the innovation numbers match, - # it averages their weights. - mate_multipoint_avg_prob: 0.3 - # Probability of mating similar to a standard single point CROSSOVER operator. Traits are averaged as in the previous two - # mating methods. A Gene is chosen in the smaller Genome for splitting. When the Gene is reached, it is averaged with - # the matching Gene from the larger Genome, if one exists. Then every other Gene is taken from the larger Genome. - mate_singlepoint_prob: 0.3 - - # Probability of mating without mutation - mate_only_prob: 0.2 - - # Probability of forcing selection of ONLY links that are naturally recurrent - recur_only_prob: 0.0 - - # The number of babies to stolen off to the champions - babies_stolen: 0 - # The population size as a number of organisms - pop_size: 200 - # Age when Species starts to be penalized - dropoff_age: 50 - # Number of tries mutate_add_link will attempt to find an open link - newlink_tries: 50 - # Tells to print population to file every n generations - print_every: 10 - - # The number of runs to average over in an experiment - num_runs: 100 - # The number of epochs (generations) to execute training - num_generations: 100 - - # The epoch's executor type to apply [sequential, parallel] - epoch_executor: sequential - - # The genome compatibility method to use [linear, fast]. The later is best for bigger genomes - genome_compat_method: fast - - # The log level - log_level: Info - - # The nodes activation functions list to choose from (activation function -> it's selection probability) - node_activators: - - SigmoidBipolarActivation 0.25 - - GaussianBipolarActivation 0.35 - - LinearAbsActivation 0.15 - - SineActivation 0.25 - ######################################## # The HyperNEAT specific configuration # ######################################## -hyperneat: - # The threshold value to indicate which links should be included - link_threshold: 0.2 +# The threshold value to indicate which links should be included +link_threshold: 0.2 - # The weight range defines the minimum and maximum values for weights on substrate connections - weight_range: 3 +# The weight range defines the minimum and maximum values for weights on substrate connections +weight_range: 3 - # The substrate activation function, determines which activation function each node in the substrate will have. - substrate_activator: SigmoidSteepenedActivation +# The substrate activation function, determines which activation function each node in the substrate will have. +substrate_activator: SigmoidSteepenedActivation ########################################### # The ES-HyperNEAT specific configuration # ########################################### -es-hyperneat: - # InitialDepth defines the initial ES-HyperNEAT sample resolution. - initial_depth: 3 - # Maximal ES-HyperNEAT sample resolution if the variance is still higher than the given division threshold - maximal_depth: 5 - - # DivisionThreshold defines the substrate division threshold. - division_threshold: 0.01 - # VarianceThreshold defines the variance threshold for the initial sampling. - variance_threshold: 0.03 - # BandingThreshold defines the threshold that determines when points are regarded to be in a band. - banding_threshold: 0.3 - - # ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. - # TODO BUG WHEN ES_ITERATIONS > 1 - es_iterations: 1 +# InitialDepth defines the initial ES-HyperNEAT sample resolution. +initial_depth: 3 +# Maximal ES-HyperNEAT sample resolution if the variance is still higher than the given division threshold +maximal_depth: 5 + +# DivisionThreshold defines the substrate division threshold. +division_threshold: 0.01 +# VarianceThreshold defines the variance threshold for the initial sampling. +variance_threshold: 0.03 +# BandingThreshold defines the threshold that determines when points are regarded to be in a band. +banding_threshold: 0.3 + +# ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. +# TODO BUG WHEN ES_ITERATIONS > 1 +es_iterations: 1 diff --git a/data/test/test_hyper.neat.yml b/data/test/test_hyper.neat.yml index e0302e2..43335ee 100644 --- a/data/test/test_hyper.neat.yml +++ b/data/test/test_hyper.neat.yml @@ -1,112 +1,11 @@ -########################################## -# The common NEAT specific configuration # -########################################## -neat: - # Probability of mutating a single trait param - trait_param_mut_prob: 0.5 - # Power of mutation on a single trait param - trait_mutation_power: 1.0 - # The power of a link weight mutation - weight_mut_power: 2.5 - - # 3 global coefficients are used to determine the formula for computing the compatibility between 2 genomes. - # The formula is: disjoint_coeff * pdg + excess_coeff * peg + mutdiff_coeff * mdmg. - # See the Compatibility method in the Genome class for more info - # They can be thought of as the importance of disjoint Genes, excess Genes, and parametric difference between Genes of - # the same function, respectively. - disjoint_coeff: 1.0 - excess_coeff: 1.0 - mutdiff_coeff: 0.4 - - # This global tells compatibility threshold under which two Genomes are considered the same species - compat_threshold: 3.0 - # How much does age matter? Gives a fitness boost up to some young age (niching). If it is 1, then young species get no fitness boost. - age_significance: 1.0 - # Percent of average fitness for survival, how many get to reproduce based on survival_thresh * pop_size - survival_thresh: 0.2 - - # Probabilities of a non-mating reproduction - mutate_only_prob: 0.25 - # Probability of genome trait mutation - mutate_random_trait_prob: 0.1 - # Probability of link trait mutation - mutate_link_trait_prob: 0.1 - # Probability of node trait mutation - mutate_node_trait_prob: 0.1 - # Probability of link weight value mutation - mutate_link_weights_prob: 0.9 - # Probability of enabling/disabling of specific link/gene - mutate_toggle_enable_prob: 0.0 - # Probability of finding the first disabled gene and re-enabling it - mutate_gene_reenable_prob: 0.0 - # Probability of adding new node - mutate_add_node_prob: 0.03 - # Probability of adding new link between nodes - mutate_add_link_prob: 0.08 - # Probability of making connections from disconnected sensors (input, bias type neurons) - mutate_connect_sensors: 0.5 - - # Probability of mating between different species - interspecies_mate_rate: 0.001 - # Probability of mating this Genome with another Genome g. For every point in each Genome, where each Genome shares - # the innovation number, the Gene is chosen randomly from either parent. If one parent has an innovation absent in - # the other, the baby may inherit the innovation if it is from the more fit parent. - mate_multipoint_prob: 0.3 - # Probability of mating like in multipoint, but instead of selecting one or the other when the innovation numbers match, - # it averages their weights. - mate_multipoint_avg_prob: 0.3 - # Probability of mating similar to a standard single point CROSSOVER operator. Traits are averaged as in the previous two - # mating methods. A Gene is chosen in the smaller Genome for splitting. When the Gene is reached, it is averaged with - # the matching Gene from the larger Genome, if one exists. Then every other Gene is taken from the larger Genome. - mate_singlepoint_prob: 0.3 - - # Probability of mating without mutation - mate_only_prob: 0.2 - - # Probability of forcing selection of ONLY links that are naturally recurrent - recur_only_prob: 0.0 - - # The number of babies to stolen off to the champions - babies_stolen: 0 - # The population size as a number of organisms - pop_size: 200 - # Age when Species starts to be penalized - dropoff_age: 50 - # Number of tries mutate_add_link will attempt to find an open link - newlink_tries: 50 - # Tells to print population to file every n generations - print_every: 10 - - # The number of runs to average over in an experiment - num_runs: 100 - # The number of epochs (generations) to execute training - num_generations: 100 - - # The epoch's executor type to apply [sequential, parallel] - epoch_executor: sequential - - # The genome compatibility method to use [linear, fast]. The later is best for bigger genomes - genome_compat_method: fast - - # The log level - log_level: Info - - # The nodes activation functions list to choose from (activation function -> it's selection probability) - node_activators: - - SigmoidBipolarActivation 0.25 - - GaussianBipolarActivation 0.35 - - LinearAbsActivation 0.15 - - SineActivation 0.25 - ######################################## # The HyperNEAT specific configuration # ######################################## -hyperneat: - # The threshold value to indicate which links should be included - link_threshold: 0.2 +# The threshold value to indicate which links should be included +link_threshold: 0.2 - # The weight range defines the minimum and maximum values for weights on substrate connections - weight_range: 3 +# The weight range defines the minimum and maximum values for weights on substrate connections +weight_range: 3 - # The substrate activation function, determines which activation function each node in the substrate will have. - substrate_activator: SigmoidSteepenedActivation \ No newline at end of file +# The substrate activation function, determines which activation function each node in the substrate will have. +substrate_activator: SigmoidSteepenedActivation \ No newline at end of file diff --git a/eshyperneat/context.go b/eshyperneat/context.go new file mode 100644 index 0000000..f0c4288 --- /dev/null +++ b/eshyperneat/context.go @@ -0,0 +1,23 @@ +package eshyperneat + +import "context" + +// key is an unexported type for keys defined in this package. +// This prevents collisions with keys defined in other packages. +type key int + +// esHyperNeatOptionsKey is the key for eshyperneat.Options value in Contexts. It is +// unexported; clients use eshyperneat.NewContext and eshyperneat.FromContext +// instead of using this key directly. +var esHyperNeatOptionsKey key + +// NewContext returns a new Context that carries value of ES-HyperNEAT options. +func NewContext(ctx context.Context, opts *Options) context.Context { + return context.WithValue(ctx, esHyperNeatOptionsKey, opts) +} + +// FromContext returns the ES-HyperNEAT Options value stored in ctx, if any. +func FromContext(ctx context.Context) (*Options, bool) { + u, ok := ctx.Value(esHyperNeatOptionsKey).(*Options) + return u, ok +} diff --git a/eshyperneat/es_hyper_neat.go b/eshyperneat/es_hyper_neat.go index 67c778f..6162877 100644 --- a/eshyperneat/es_hyper_neat.go +++ b/eshyperneat/es_hyper_neat.go @@ -2,79 +2,60 @@ package eshyperneat import ( - "bytes" - "errors" - "github.com/spf13/viper" + "github.com/pkg/errors" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" + "gopkg.in/yaml.v3" "io" + "io/ioutil" + "os" ) -// ESHyperNEATContext ES-HyperNEAT execution context -type ESHyperNEATContext struct { - // The included HyperNEAT context - *hyperneat.HyperNEATContext +// Options ES-HyperNEAT execution options +type Options struct { + // The included HyperNEAT options + *hyperneat.Options `yaml:",inline"` // InitialDepth defines the initial ES-HyperNEAT sample resolution. - InitialDepth int + InitialDepth int `yaml:"initial_depth"` // Maximal ES-HyperNEAT sample resolution if the variance is still higher than the given division threshold - MaximalDepth int + MaximalDepth int `yaml:"maximal_depth"` // DivisionThreshold defines the division threshold. If the variance in a region is greater than this value, after // the initial resolution is reached, ES-HyperNEAT will sample down further (values greater than 1.0 will disable // this feature). Note that sampling at really high resolutions can become computationally expensive. - DivisionThreshold float64 + DivisionThreshold float64 `yaml:"division_threshold"` // VarianceThreshold defines the variance threshold for the initial sampling. The bigger this value the less new // connections will be added directly and the more chances that the new collection will be included in bands // (see BandingThreshold) - VarianceThreshold float64 + VarianceThreshold float64 `yaml:"variance_threshold"` // BandingThreshold defines the threshold that determines when points are regarded to be in a band. If the point // is in the band then no new connection will be added and as result no new hidden node will be introduced. - // The bigger this value the less connections/hidden nodes will be added, i.e. wide bands approximation. - BandingThreshold float64 + // The bigger this value the fewer connections/hidden nodes will be added, i.e. wide bands approximation. + BandingThreshold float64 `yaml:"banding_threshold"` // ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. - ESIterations int + ESIterations int `yaml:"es_iterations"` } -// Load loads ESHyperNEAT context options from provided reader -func Load(r io.Reader) (*ESHyperNEATContext, error) { - var buff bytes.Buffer - tee := io.TeeReader(r, &buff) - - // load HyperNEAT options - hCtx, err := hyperneat.Load(tee) +// LoadYAMLOptions is to load ES-HyperNEAT options from provided reader +func LoadYAMLOptions(r io.Reader) (*Options, error) { + content, err := ioutil.ReadAll(r) if err != nil { return nil, err } - - // load ES options - ctx := &ESHyperNEATContext{HyperNEATContext: hCtx} - if err := ctx.load(&buff); err != nil { - return nil, err + // read options + var opts Options + if err = yaml.Unmarshal(content, &opts); err != nil { + return nil, errors.Wrap(err, "Failed to decode ES-HyperNEAT options from YAML.") } - - return ctx, nil + return &opts, nil } -func (e *ESHyperNEATContext) load(r io.Reader) error { - viper.SetConfigType("YAML") - err := viper.ReadConfig(r) +// LoadYAMLConfigFile is to load ES-HyperNEAT options from provided configuration file +func LoadYAMLConfigFile(path string) (*Options, error) { + configFile, err := os.Open(path) if err != nil { - return err - } - v := viper.Sub("es-hyperneat") - if v == nil { - return errors.New("es-hyperneat subsection not found in configuration") + return nil, errors.Wrap(err, "Failed to open ES-HyperNEAT configuration file.") } - - e.InitialDepth = v.GetInt("initial_depth") - e.MaximalDepth = v.GetInt("maximal_depth") - - e.DivisionThreshold = v.GetFloat64("division_threshold") - e.VarianceThreshold = v.GetFloat64("variance_threshold") - e.BandingThreshold = v.GetFloat64("banding_threshold") - - e.ESIterations = v.GetInt("es_iterations") - - return nil + return LoadYAMLOptions(configFile) } diff --git a/eshyperneat/es_hyper_neat_test.go b/eshyperneat/es_hyper_neat_test.go index 3b31ef8..20d74f4 100644 --- a/eshyperneat/es_hyper_neat_test.go +++ b/eshyperneat/es_hyper_neat_test.go @@ -3,25 +3,39 @@ package eshyperneat import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/neat/utils" + "github.com/yaricom/goNEAT/v2/neat/math" "os" "testing" ) -func TestESHyperNEATContext_LoadFullContext(t *testing.T) { - r, err := os.Open("../data/test/test_es_hyper.neat.yml") - require.NoError(t, err, "failed to open config file") +const EsHyperNeatCfgPath = "../data/test/test_es_hyper.neat.yml" - esCtx, err := Load(r) - require.NoError(t, err, "failed to load context") +func TestLoadYAMLConfigFile(t *testing.T) { + opts, err := LoadYAMLConfigFile(EsHyperNeatCfgPath) + require.NoError(t, err, "failed to load options from config file") // check loaded values - assert.Equal(t, 3, esCtx.InitialDepth) - assert.Equal(t, 5, esCtx.MaximalDepth) - assert.Equal(t, 0.01, esCtx.DivisionThreshold) - assert.Equal(t, 0.03, esCtx.VarianceThreshold) - assert.Equal(t, 0.3, esCtx.BandingThreshold) - assert.Equal(t, 1, esCtx.ESIterations) - - assert.Equal(t, utils.SigmoidSteepenedActivation, esCtx.HyperNEATContext.SubstrateActivator) + checkEsHyperNeatOptions(opts, t) +} + +func TestLoadYAMLOptions(t *testing.T) { + configFile, err := os.Open(EsHyperNeatCfgPath) + assert.NoError(t, err, "Failed to open ES-HyperNEAT configuration file.") + + opts, err := LoadYAMLOptions(configFile) + assert.NoError(t, err, "failed to load options from config file") + + // check loaded values + checkEsHyperNeatOptions(opts, t) +} + +func checkEsHyperNeatOptions(opts *Options, t *testing.T) { + assert.Equal(t, 3, opts.InitialDepth) + assert.Equal(t, 5, opts.MaximalDepth) + assert.Equal(t, 0.01, opts.DivisionThreshold) + assert.Equal(t, 0.03, opts.VarianceThreshold) + assert.Equal(t, 0.3, opts.BandingThreshold) + assert.Equal(t, 1, opts.ESIterations) + + assert.Equal(t, math.SigmoidSteepenedActivation, opts.SubstrateActivator.SubstrateActivationType) } diff --git a/hyperneat/context.go b/hyperneat/context.go new file mode 100644 index 0000000..8aecf96 --- /dev/null +++ b/hyperneat/context.go @@ -0,0 +1,23 @@ +package hyperneat + +import "context" + +// key is an unexported type for keys defined in this package. +// This prevents collisions with keys defined in other packages. +type key int + +// hyperNeatOptionsKey is the key for hyperneat.Options value in Contexts. It is +// unexported; clients use hyperneat.NewContext and hyperneat.FromContext +// instead of using this key directly. +var hyperNeatOptionsKey key + +// NewContext returns a new Context that carries value of HyperNEAT options. +func NewContext(ctx context.Context, opts *Options) context.Context { + return context.WithValue(ctx, hyperNeatOptionsKey, opts) +} + +// FromContext returns the HyperNEAT Options value stored in ctx, if any. +func FromContext(ctx context.Context) (*Options, bool) { + u, ok := ctx.Value(hyperNeatOptionsKey).(*Options) + return u, ok +} diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index 414e814..cbdd9b6 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -2,69 +2,59 @@ package hyperneat import ( - "bytes" - "errors" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" "io" + "io/ioutil" + "os" - "github.com/spf13/viper" - "github.com/yaricom/goNEAT/neat" - "github.com/yaricom/goNEAT/neat/utils" + "github.com/yaricom/goNEAT/v2/neat/math" ) -// HyperNEATContext The HyperNEAT execution context -type HyperNEATContext struct { - // The NEAT context included - *neat.NeatContext +type SubstrateActivatorType struct { + SubstrateActivationType math.NodeActivationType +} +// Options The HyperNEAT execution options +type Options struct { // The threshold value to indicate which links should be included - LinkThreshold float64 + LinkThreshold float64 `yaml:"link_threshold"` // The weight range defines the minimum and maximum values for weights on substrate connections, they go // from -WeightRange to +WeightRange, and can be any integer - WeightRange float64 + WeightRange float64 `yaml:"weight_range"` // The substrate activation function - SubstrateActivator utils.NodeActivationType + SubstrateActivator SubstrateActivatorType `yaml:"substrate_activator"` } -// Load is to read HyperNEAT context options from the provided reader -func Load(r io.Reader) (*HyperNEATContext, error) { - var buff bytes.Buffer - tee := io.TeeReader(r, &buff) - - // NEAT context loading - nCtx := &neat.NeatContext{} - if err := nCtx.LoadContext(tee); err != nil { +// LoadYAMLOptions is to read HyperNEAT options from the provided reader +func LoadYAMLOptions(r io.Reader) (*Options, error) { + content, err := ioutil.ReadAll(r) + if err != nil { return nil, err } - - // Load HyperNEAT options - ctx := &HyperNEATContext{NeatContext: nCtx} - if err := ctx.load(&buff); err != nil { - return nil, err + // read options + var opts Options + if err = yaml.Unmarshal(content, &opts); err != nil { + return nil, errors.Wrap(err, "Failed to decode HyperNEAT options from YAML.") } - return ctx, nil + return &opts, nil } -// Loads only HyperNEAT context from provided configuration data -func (h *HyperNEATContext) load(r io.Reader) error { - viper.SetConfigType("YAML") - err := viper.ReadConfig(r) +// LoadYAMLConfigFile is to load ES-HyperNEAT options from provided configuration file +func LoadYAMLConfigFile(path string) (*Options, error) { + configFile, err := os.Open(path) if err != nil { - return err + return nil, errors.Wrap(err, "Failed to open HyperNEAT configuration file.") } - v := viper.Sub("hyperneat") - if v == nil { - return errors.New("hyperneat subsection not found in configuration") - } - - h.LinkThreshold = v.GetFloat64("link_threshold") - h.WeightRange = v.GetFloat64("weight_range") + return LoadYAMLOptions(configFile) +} - // read substrate activator - subAct := v.GetString("substrate_activator") - if h.SubstrateActivator, err = utils.NodeActivators.ActivationTypeFromName(subAct); err != nil { - return err +func (s *SubstrateActivatorType) UnmarshalYAML(value *yaml.Node) error { + if activationType, err := math.NodeActivators.ActivationTypeFromName(value.Value); err != nil { + return errors.Wrap(err, "Failed to decode substrate activator function from HyperNEAT options.") + } else { + s.SubstrateActivationType = activationType } - return nil } diff --git a/hyperneat/hyper_neat_test.go b/hyperneat/hyper_neat_test.go index ea0f1ce..8c8afdb 100644 --- a/hyperneat/hyper_neat_test.go +++ b/hyperneat/hyper_neat_test.go @@ -3,20 +3,34 @@ package hyperneat import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/neat/utils" + "github.com/yaricom/goNEAT/v2/neat/math" "os" "testing" ) -func TestHyperNEATContext_LoadContext(t *testing.T) { - r, err := os.Open("../data/test/test_es_hyper.neat.yml") +const hyperNeatCfgPath = "../data/test/test_hyper.neat.yml" + +func TestLoadYAMLOptions(t *testing.T) { + r, err := os.Open(hyperNeatCfgPath) require.NoError(t, err, "failed to open config file") - ctx, err := Load(r) - require.NoError(t, err, "failed to load context") + opts, err := LoadYAMLOptions(r) + require.NoError(t, err, "failed to load HyperNEAT options") // check values - assert.Equal(t, utils.SigmoidSteepenedActivation, ctx.SubstrateActivator) - assert.Equal(t, 0.2, ctx.LinkThreshold) - assert.Equal(t, 3.0, ctx.WeightRange) + checkHyperNeatOptions(opts, t) +} + +func TestLoadYAMLConfigFile(t *testing.T) { + opts, err := LoadYAMLConfigFile(hyperNeatCfgPath) + require.NoError(t, err, "failed to load HyperNEAT options") + + // check values + checkHyperNeatOptions(opts, t) +} + +func checkHyperNeatOptions(opts *Options, t *testing.T) { + assert.Equal(t, math.SigmoidSteepenedActivation, opts.SubstrateActivator.SubstrateActivationType) + assert.Equal(t, 0.2, opts.LinkThreshold) + assert.Equal(t, 3.0, opts.WeightRange) } From c70a01579d3870e882b1f3619598c679739c62d2 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 18:56:27 +0300 Subject: [PATCH 10/57] Updated experiment executor and implementation of the retina experiment example. --- .github/codecov.yml | 2 +- Makefile | 2 +- data/retina/es_hyper.neat.yml | 233 +++++++++--------- .../retina/cppn_network_builder.go | 21 +- {experiments => examples}/retina/dataset.go | 0 .../retina/dataset_test.go | 0 .../retina/environment.go | 4 +- .../retina/environment_test.go | 0 {experiments => examples}/retina/retina.go | 124 +++++----- .../retina/retina_test.go | 0 executor.go | 91 +++++-- 11 files changed, 261 insertions(+), 216 deletions(-) rename {experiments => examples}/retina/cppn_network_builder.go (75%) rename {experiments => examples}/retina/dataset.go (100%) rename {experiments => examples}/retina/dataset_test.go (100%) rename {experiments => examples}/retina/environment.go (95%) rename {experiments => examples}/retina/environment_test.go (100%) rename {experiments => examples}/retina/retina.go (64%) rename {experiments => examples}/retina/retina_test.go (100%) diff --git a/.github/codecov.yml b/.github/codecov.yml index 7897d5d..7528f3b 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,2 +1,2 @@ ignore: - - "examples/**/*" # ignoring experiments folder \ No newline at end of file + - "examples/**/*" # ignoring examples folder \ No newline at end of file diff --git a/Makefile b/Makefile index 40d5562..b96a26e 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ GORUN = $(GOCMD) run # The common parameters BINARY_NAME = eshyperneat OUT_DIR = out -LOG_LEVEL = -1 +LOG_LEVEL = info # # The retina experiment parameters diff --git a/data/retina/es_hyper.neat.yml b/data/retina/es_hyper.neat.yml index f097eed..e6b8504 100644 --- a/data/retina/es_hyper.neat.yml +++ b/data/retina/es_hyper.neat.yml @@ -1,131 +1,128 @@ ########################################## # The common NEAT specific configuration # ########################################## -neat: - # Probability of mutating a single trait param - trait_param_mut_prob: 0.5 - # Power of mutation on a single trait param - trait_mutation_power: 1.0 - # The power of a link weight mutation - weight_mut_power: 2.5 - - # 3 global coefficients are used to determine the formula for computing the compatibility between 2 genomes. - # The formula is: disjoint_coeff * pdg + excess_coeff * peg + mutdiff_coeff * mdmg. - # See the Compatibility method in the Genome class for more info - # They can be thought of as the importance of disjoint Genes, excess Genes, and parametric difference between Genes of - # the same function, respectively. - disjoint_coeff: 1.0 - excess_coeff: 1.0 - mutdiff_coeff: 0.4 - - # This global tells compatibility threshold under which two Genomes are considered the same species - compat_threshold: 3.0 - # How much does age matter? Gives a fitness boost up to some young age (niche). If it is 1, then young species get no fitness boost. - age_significance: 1.0 - # Percent of average fitness for survival, how many get to reproduce based on survival_thresh * pop_size - survival_thresh: 0.2 - - # Probabilities of a non-mating reproduction - mutate_only_prob: 0.25 - # Probability of genome trait mutation - mutate_random_trait_prob: 0.1 - # Probability of link trait mutation - mutate_link_trait_prob: 0.1 - # Probability of node trait mutation - mutate_node_trait_prob: 0.1 - # Probability of link weight value mutation - mutate_link_weights_prob: 0.9 - # Probability of enabling/disabling of specific link/gene - mutate_toggle_enable_prob: 0.0 - # Probability of finding the first disabled gene and re-enabling it - mutate_gene_reenable_prob: 0.0 - # Probability of adding new node - mutate_add_node_prob: 0.03 - # Probability of adding new link between nodes - mutate_add_link_prob: 0.08 - # Probability of making connections from disconnected sensors (input, bias type neurons) - mutate_connect_sensors: 0.5 - - # Probability of mating between different species - interspecies_mate_rate: 0.001 - # Probability of mating this Genome with another Genome g. For every point in each Genome, where each Genome shares - # the innovation number, the Gene is chosen randomly from either parent. If one parent has an innovation absent in - # the other, the baby may inherit the innovation if it is from the more fit parent. - mate_multipoint_prob: 0.3 - # Probability of mating like in multipoint, but instead of selecting one or the other when the innovation numbers match, - # it averages their weights. - mate_multipoint_avg_prob: 0.3 - # Probability of mating similar to a standard single point CROSSOVER operator. Traits are averaged as in the previous two - # mating methods. A Gene is chosen in the smaller Genome for splitting. When the Gene is reached, it is averaged with - # the matching Gene from the larger Genome, if one exists. Then every other Gene is taken from the larger Genome. - mate_singlepoint_prob: 0.3 - - # Probability of mating without mutation - mate_only_prob: 0.2 - - # Probability of forcing selection of ONLY links that are naturally recurrent - recur_only_prob: 0.0 - - # The number of babies to stolen off to the champions - babies_stolen: 0 - # The population size as a number of organisms - pop_size: 300 - # Age when Species starts to be penalized - dropoff_age: 50 - # Number of tries mutate_add_link will attempt to find an open link - newlink_tries: 50 - # Tells to print population to file every n generations - print_every: 10 - - # The number of runs to average over in an experiment - num_runs: 100 - # The number of epochs (generations) to execute training - num_generations: 100 - - # The epoch's executor type to apply [sequential, parallel] - epoch_executor: sequential - - # The genome compatibility method to use [linear, fast]. The later is best for bigger genomes - genome_compat_method: fast - - # The log level - log_level: Info - - # The nodes activation functions list to choose from (activation function -> it's selection probability) - node_activators: - - SigmoidBipolarActivation 0.25 - - GaussianBipolarActivation 0.35 - - LinearAbsActivation 0.15 - - SineActivation 0.25 +# Probability of mutating a single trait param +trait_param_mut_prob: 0.5 +# Power of mutation on a single trait param +trait_mutation_power: 1.0 +# The power of a link weight mutation +weight_mut_power: 2.5 + +# 3 global coefficients are used to determine the formula for computing the compatibility between 2 genomes. +# The formula is: disjoint_coeff * pdg + excess_coeff * peg + mutdiff_coeff * mdmg. +# See the Compatibility method in the Genome class for more info +# They can be thought of as the importance of disjoint Genes, excess Genes, and parametric difference between Genes of +# the same function, respectively. +disjoint_coeff: 1.0 +excess_coeff: 1.0 +mutdiff_coeff: 0.4 + +# This global tells compatibility threshold under which two Genomes are considered the same species +compat_threshold: 3.0 +# How much does age matter? Gives a fitness boost up to some young age (niche). If it is 1, then young species get no fitness boost. +age_significance: 1.0 +# Percent of average fitness for survival, how many get to reproduce based on survival_thresh * pop_size +survival_thresh: 0.2 + +# Probabilities of a non-mating reproduction +mutate_only_prob: 0.25 +# Probability of genome trait mutation +mutate_random_trait_prob: 0.1 +# Probability of link trait mutation +mutate_link_trait_prob: 0.1 +# Probability of node trait mutation +mutate_node_trait_prob: 0.1 +# Probability of link weight value mutation +mutate_link_weights_prob: 0.9 +# Probability of enabling/disabling of specific link/gene +mutate_toggle_enable_prob: 0.0 +# Probability of finding the first disabled gene and re-enabling it +mutate_gene_reenable_prob: 0.0 +# Probability of adding new node +mutate_add_node_prob: 0.03 +# Probability of adding new link between nodes +mutate_add_link_prob: 0.08 +# Probability of making connections from disconnected sensors (input, bias type neurons) +mutate_connect_sensors: 0.5 + +# Probability of mating between different species +interspecies_mate_rate: 0.001 +# Probability of mating this Genome with another Genome g. For every point in each Genome, where each Genome shares +# the innovation number, the Gene is chosen randomly from either parent. If one parent has an innovation absent in +# the other, the baby may inherit the innovation if it is from the more fit parent. +mate_multipoint_prob: 0.3 +# Probability of mating like in multipoint, but instead of selecting one or the other when the innovation numbers match, +# it averages their weights. +mate_multipoint_avg_prob: 0.3 +# Probability of mating similar to a standard single point CROSSOVER operator. Traits are averaged as in the previous two +# mating methods. A Gene is chosen in the smaller Genome for splitting. When the Gene is reached, it is averaged with +# the matching Gene from the larger Genome, if one exists. Then every other Gene is taken from the larger Genome. +mate_singlepoint_prob: 0.3 + +# Probability of mating without mutation +mate_only_prob: 0.2 + +# Probability of forcing selection of ONLY links that are naturally recurrent +recur_only_prob: 0.0 + +# The number of babies to stolen off to the champions +babies_stolen: 0 +# The population size as a number of organisms +pop_size: 300 +# Age when Species starts to be penalized +dropoff_age: 50 +# Number of tries mutate_add_link will attempt to find an open link +newlink_tries: 50 +# Tells to print population to file every n generations +print_every: 10 + +# The number of runs to average over in an experiment +num_runs: 100 +# The number of epochs (generations) to execute training +num_generations: 100 + +# The epoch's executor type to apply [sequential, parallel] +epoch_executor: sequential + +# The genome compatibility method to use [linear, fast]. The latter is best for bigger genomes +genome_compat_method: fast + +# The log level +log_level: info + +# The nodes activation functions list to choose from (activation function -> it's selection probability) +node_activators: + - SigmoidBipolarActivation 0.25 + - GaussianBipolarActivation 0.35 + - LinearAbsActivation 0.15 + - SineActivation 0.25 ######################################## # The HyperNEAT specific configuration # ######################################## -hyperneat: - # The threshold value to indicate which links should be included - link_threshold: 0.2 +# The threshold value to indicate which links should be included +link_threshold: 0.2 - # The weight range defines the minimum and maximum values for weights on substrate connections - weight_range: 3 +# The weight range defines the minimum and maximum values for weights on substrate connections +weight_range: 3 - # The substrate activation function, determines which activation function each node in the substrate will have. - substrate_activator: SigmoidBipolarActivation +# The substrate activation function, determines which activation function each node in the substrate will have. +substrate_activator: SigmoidBipolarActivation ########################################### # The ES-HyperNEAT specific configuration # ########################################### -es-hyperneat: - # InitialDepth defines the initial ES-HyperNEAT sample resolution. - initial_depth: 2 - # Maximal ES-HyperNEAT sample resolution if the variance is still higher than the given division threshold - maximal_depth: 3 - - # DivisionThreshold defines the substrate division threshold. - division_threshold: 0.5 - # VarianceThreshold defines the variance threshold for the initial sampling. - variance_threshold: 0.03 - # BandingThreshold defines the threshold that determines when points are regarded to be in a band. - banding_threshold: 0.3 - - # ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. - es_iterations: 1 +# InitialDepth defines the initial ES-HyperNEAT sample resolution. +initial_depth: 2 +# Maximal ES-HyperNEAT sample resolution if the variance is still higher than the given division threshold +maximal_depth: 3 + +# DivisionThreshold defines the substrate division threshold. +division_threshold: 0.5 +# VarianceThreshold defines the variance threshold for the initial sampling. +variance_threshold: 0.03 +# BandingThreshold defines the threshold that determines when points are regarded to be in a band. +banding_threshold: 0.3 + +# ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. +es_iterations: 1 diff --git a/experiments/retina/cppn_network_builder.go b/examples/retina/cppn_network_builder.go similarity index 75% rename from experiments/retina/cppn_network_builder.go rename to examples/retina/cppn_network_builder.go index 163b512..27527de 100644 --- a/experiments/retina/cppn_network_builder.go +++ b/examples/retina/cppn_network_builder.go @@ -2,12 +2,11 @@ package retina import ( "fmt" - "github.com/yaricom/goESHyperNEAT/v2/cppn" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goNEAT/neat/genetics" - "github.com/yaricom/goNEAT/neat/network" - "github.com/yaricom/goNEAT/neat/utils" + "github.com/yaricom/goNEAT/v2/neat/genetics" + "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v2/neat/network" ) const debug = true @@ -16,21 +15,21 @@ const debug = true type CPPNNetworkBuilder struct { UseLEO bool SubstrateLayout cppn.EvolvableSubstrateLayout - NodesActivation utils.NodeActivationType - Context *eshyperneat.ESHyperNEATContext + NodesActivation math.NodeActivationType + Context *eshyperneat.Options // New Graph Builder generator function. // Note: Graph Builder is used to store graphs in a user-friendly format NewGraphBuilder func() *cppn.SubstrateGraphBuilder } -//CreateANNFromCPPNOrganism creates a NetworkSolver (ANN) by querying the Organism.Phenotype cppnNetwork by the ESHyperNeat Algorithm -func (builder *CPPNNetworkBuilder) CreateANNFromCPPNOrganism(cppnOrganism *genetics.Organism) (network.NetworkSolver, error) { +// CreateANNFromCPPNOrganism creates a NetworkSolver (ANN) by querying the Organism.Phenotype cppnNetwork by the ESHyperNeat Algorithm +func (builder *CPPNNetworkBuilder) CreateANNFromCPPNOrganism(cppnOrganism *genetics.Organism) (network.Solver, error) { return builder.CreateANNFromCPPN(cppnOrganism.Phenotype) } -//CreateANNFromCPPN creates a NetworkSolver (ANN) by querying the cppnNetwork by the ESHyperNeat Algorithm -func (builder *CPPNNetworkBuilder) CreateANNFromCPPN(cppnNetwork *network.Network) (network.NetworkSolver, error) { +// CreateANNFromCPPN creates a NetworkSolver (ANN) by querying the cppnNetwork by the ESHyperNeat Algorithm +func (builder *CPPNNetworkBuilder) CreateANNFromCPPN(cppnNetwork *network.Network) (network.Solver, error) { // create substrate which will be connected to form the network substrate := cppn.NewEvolvableSubstrate(builder.SubstrateLayout, builder.NodesActivation) cppnFastNetwork, err := cppnNetwork.FastNetworkSolver() @@ -42,7 +41,7 @@ func (builder *CPPNNetworkBuilder) CreateANNFromCPPN(cppnNetwork *network.Networ graphBuilder := builder.NewGraphBuilder() // unwrap graphBuilder. graphBuilder may not be provided (its nil) - var annSolver network.NetworkSolver + var annSolver network.Solver if graphBuilder == nil { // create our ann by querying the cppn network and applying the eshypernet quadtree/pruning/banding algorithm annSolver, err = substrate.CreateNetworkSolver( diff --git a/experiments/retina/dataset.go b/examples/retina/dataset.go similarity index 100% rename from experiments/retina/dataset.go rename to examples/retina/dataset.go diff --git a/experiments/retina/dataset_test.go b/examples/retina/dataset_test.go similarity index 100% rename from experiments/retina/dataset_test.go rename to examples/retina/dataset_test.go diff --git a/experiments/retina/environment.go b/examples/retina/environment.go similarity index 95% rename from experiments/retina/environment.go rename to examples/retina/environment.go index 0c66c07..bb4783a 100644 --- a/experiments/retina/environment.go +++ b/examples/retina/environment.go @@ -24,12 +24,12 @@ func (s DetectionSide) String() string { type Environment struct { visualObjects []VisualObject inputSize int - context *eshyperneat.ESHyperNEATContext + context *eshyperneat.Options } // NewRetinaEnvironment creates a new Retina Environment with a dataset of all possible Visual Object with specified // number of inputs to be acquired from provided objects -func NewRetinaEnvironment(dataSet []VisualObject, inputSize int, context *eshyperneat.ESHyperNEATContext) (*Environment, error) { +func NewRetinaEnvironment(dataSet []VisualObject, inputSize int, context *eshyperneat.Options) (*Environment, error) { // check that provided visual objects has data points equal to the inputSize for _, o := range dataSet { if len(o.data) != inputSize { diff --git a/experiments/retina/environment_test.go b/examples/retina/environment_test.go similarity index 100% rename from experiments/retina/environment_test.go rename to examples/retina/environment_test.go diff --git a/experiments/retina/retina.go b/examples/retina/retina.go similarity index 64% rename from experiments/retina/retina.go rename to examples/retina/retina.go index a14c2f0..e5624c7 100644 --- a/experiments/retina/retina.go +++ b/examples/retina/retina.go @@ -5,10 +5,11 @@ import ( "errors" "fmt" "github.com/yaricom/goESHyperNEAT/v2/cppn" - "github.com/yaricom/goNEAT/experiments" - "github.com/yaricom/goNEAT/neat" - "github.com/yaricom/goNEAT/neat/genetics" - "github.com/yaricom/goNEAT/neat/network" + "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" "math" "os" ) @@ -26,15 +27,31 @@ type generationEvaluator struct { } // NewGenerationEvaluator is to create new generations evaluator for retina experiment. -func NewGenerationEvaluator(outDir string, env *Environment) experiments.GenerationEvaluator { - return &generationEvaluator{ +func NewGenerationEvaluator(outDir string, env *Environment) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { + evaluator := &generationEvaluator{ outDir: outDir, env: env, } + return evaluator, evaluator +} + +// TrialRunStarted invoked to notify that new trial run just started. Invoked before any epoch evaluation in that trial run +func (e *generationEvaluator) TrialRunStarted(_ *experiment.Trial) { + // just stub +} + +// TrialRunFinished invoked to notify that the trial run just finished. Invoked after all epochs evaluated or successful solver found. +func (e *generationEvaluator) TrialRunFinished(_ *experiment.Trial) { + // just stub +} + +// EpochEvaluated invoked to notify that evaluation of specific epoch completed. +func (e *generationEvaluator) EpochEvaluated(_ *experiment.Trial, _ *experiment.Generation) { + // just stub } // GenerationEvaluate evaluates a population of organisms and prints their performance on the retina experiment -func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population, epoch *experiments.Generation, context *neat.NeatContext) (err error) { +func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population, epoch *experiment.Generation, options *neat.Options) (err error) { // Evaluate each organism on a test for idx, organism := range population.Organisms { isWinner, err := e.organismEvaluate(organism) @@ -47,17 +64,12 @@ func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population epoch.Solved = true epoch.WinnerNodes = len(organism.Genotype.Nodes) epoch.WinnerGenes = organism.Genotype.Extrons() - epoch.WinnerEvals = context.PopSize*epoch.Id + organism.Genotype.Id + epoch.WinnerEvals = options.PopSize*epoch.Id + organism.Genotype.Id epoch.Best = organism if epoch.WinnerNodes == 9 { // You could dump out optimal genomes here if desired - optPath := fmt.Sprintf("%s/%s_%d-%d", experiments.OutDirForTrial(e.outDir, epoch.TrialId), - "retina_optimal", organism.Phenotype.NodeCount(), organism.Phenotype.LinkCount()) - file, err := os.Create(optPath) - if err != nil { + if optPath, err := utils.WriteGenomePlain("xor_optimal", e.outDir, organism, epoch); err != nil { neat.ErrorLog(fmt.Sprintf("Failed to dump optimal genome, reason: %s\n", err)) - } else if err = organism.Genotype.Write(file); err != nil { - neat.ErrorLog("Failed to save optimal genotype") } else { neat.InfoLog(fmt.Sprintf("Dumped optimal genome to: %s\n", optPath)) } @@ -69,47 +81,41 @@ func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population epoch.FillPopulationStatistics(population) // Only print to file every print_every generations - if epoch.Solved || epoch.Id%context.PrintEvery == 0 { - popPath := fmt.Sprintf("%s/gen_%d", experiments.OutDirForTrial(e.outDir, epoch.TrialId), epoch.Id) - file, err := os.Create(popPath) - if err != nil { + if epoch.Solved || epoch.Id%options.PrintEvery == 0 { + if _, err = utils.WritePopulationPlain(e.outDir, population, epoch); err != nil { neat.ErrorLog(fmt.Sprintf("Failed to dump population, reason: %s\n", err)) - } else { - population.WriteBySpecies(file) + return err } } if epoch.Solved { // print winner organism - for _, org := range population.Organisms { - if org.IsWinner { - // Prints the winner CPPN organism to file! - // - orgPath := fmt.Sprintf("%s/%s_%d-%d", experiments.OutDirForTrial(e.outDir, epoch.TrialId), - "retina_cppn_winner", org.Phenotype.NodeCount(), org.Phenotype.LinkCount()) - if file, err := os.Create(orgPath); err != nil { - neat.ErrorLog(err.Error()) - } else if err = org.Genotype.Write(file); err != nil { - neat.ErrorLog(fmt.Sprintf("Failed to dump winner CPPN genotype, reason: %s\n", err)) - } else { - neat.InfoLog(fmt.Sprintf("Generation #%d winner CPPN dumped to: %s\n", epoch.Id, orgPath)) - } - // Dump the winner substrate graph - // - graph := org.Data.Value.(cppn.SubstrateGraphBuilder) - nodes, _ := graph.NodesCount() - edges, _ := graph.EdgesCount() - substrPath := fmt.Sprintf("%s/%s_%d-%d.xml", experiments.OutDirForTrial(e.outDir, epoch.TrialId), - "retina_substrate_graph_winner", nodes, edges) - if file, err := os.Create(substrPath); err != nil { - neat.ErrorLog(err.Error()) - } else if err = graph.Marshal(file); err != nil { - neat.ErrorLog(fmt.Sprintf("Failed to dump winner substrate, reason: %s\n", err)) - } else { - neat.InfoLog(fmt.Sprintf("Generation #%d winner's substrate dumped to: %s\n", epoch.Id, substrPath)) - } - break - } + org := epoch.Best + if depth, err := org.Phenotype.MaxActivationDepthFast(0); err == nil { + neat.InfoLog(fmt.Sprintf("Activation depth of the winner: %d\n", depth)) + } + + genomeFile := "retina_cppn_winner" + // Prints the winner organism's Genome to the file! + if orgPath, err := utils.WriteGenomePlain(genomeFile, e.outDir, 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's genome dumped to: %s\n", epoch.Id, orgPath)) + } + + // Dump the winner substrate graph + // + graph := org.Data.Value.(cppn.SubstrateGraphBuilder) + nodes, _ := graph.NodesCount() + edges, _ := graph.EdgesCount() + substrPath := fmt.Sprintf("%s/%s_%d-%d.xml", utils.CreateOutDirForTrial(e.outDir, epoch.TrialId), + "retina_substrate_graph_winner", nodes, edges) + if file, err := os.Create(substrPath); err != nil { + neat.ErrorLog(err.Error()) + } else if err = graph.Marshal(file); err != nil { + neat.ErrorLog(fmt.Sprintf("Failed to dump winner substrate, reason: %s\n", err)) + } else { + neat.InfoLog(fmt.Sprintf("Generation #%d winner's substrate dumped to: %s\n", epoch.Id, substrPath)) } } return err @@ -130,7 +136,7 @@ func (e generationEvaluator) organismEvaluate(organism *genetics.Organism) (bool return false, err } // create ES-HyperNEAT solver - substr := cppn.NewEvolvableSubstrate(layout, e.env.context.SubstrateActivator) + substr := cppn.NewEvolvableSubstrate(layout, e.env.context.SubstrateActivator.SubstrateActivationType) graph := cppn.NewSubstrateGraphMLBuilder("retina ES-HyperNEAT", false) solver, err := substr.CreateNetworkSolver(cppnSolver, graph, e.env.context) if err != nil { @@ -189,7 +195,7 @@ func (e generationEvaluator) organismEvaluate(organism *genetics.Organism) (bool // evaluateNetwork is to evaluate provided network solver using provided visual objects to test prediction performance. // Returns the prediction loss value or error if failed to evaluate. -func evaluateNetwork(solver network.NetworkSolver, leftObj VisualObject, rightObj VisualObject) (float64, error) { +func evaluateNetwork(solver network.Solver, leftObj VisualObject, rightObj VisualObject) (float64, error) { // Create input by joining data from left and right visual objects inputs := append(leftObj.data, rightObj.data...) @@ -213,12 +219,12 @@ func evaluateNetwork(solver network.NetworkSolver, leftObj VisualObject, rightOb // evaluatePredictions returns the loss between predictions and ground truth of leftObj and rightObj func evaluatePredictions(predictions []float64, leftObj VisualObject, rightObj VisualObject) float64 { // Convert predictions[i] to 1.0 or 0.0 about 0.5 threshold - normPreds := make([]float64, len(predictions)) - for i := 0; i < len(normPreds); i++ { - if normPreds[i] >= 0.5 { - normPreds[i] = 1.0 + normPredictions := make([]float64, len(predictions)) + for i := 0; i < len(normPredictions); i++ { + if normPredictions[i] >= 0.5 { + normPredictions[i] = 1.0 } else { - normPreds[i] = 0.0 + normPredictions[i] = 0.0 } } @@ -239,8 +245,8 @@ func evaluatePredictions(predictions []float64, leftObj VisualObject, rightObj V targets[1] = 0.0 } - // Find loss as an Euclidean distance between outputs and ground truth - loss := (normPreds[0]-targets[0])*(normPreds[0]-targets[0]) + (normPreds[1]-targets[1])*(normPreds[1]-targets[1]) + // Find loss as a Euclidean distance between outputs and ground truth + loss := (normPredictions[0]-targets[0])*(normPredictions[0]-targets[0]) + (normPredictions[1]-targets[1])*(normPredictions[1]-targets[1]) flag := "match" if loss != 0 { @@ -248,7 +254,7 @@ func evaluatePredictions(predictions []float64, leftObj VisualObject, rightObj V } neat.DebugLog(fmt.Sprintf("[%.2f, %.2f] -> [%.2f, %.2f] '%s'", - targets[0], targets[1], normPreds[0], normPreds[1], flag)) + targets[0], targets[1], normPredictions[0], normPredictions[1], flag)) return loss diff --git a/experiments/retina/retina_test.go b/examples/retina/retina_test.go similarity index 100% rename from experiments/retina/retina_test.go rename to examples/retina/retina_test.go diff --git a/executor.go b/executor.go index 854d764..8df7862 100644 --- a/executor.go +++ b/executor.go @@ -1,19 +1,22 @@ package main import ( + "context" "flag" "fmt" + "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" + "github.com/yaricom/goNEAT/v2/experiment" "log" "math/rand" "os" + "os/signal" "strings" + "syscall" "time" - "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goESHyperNEAT/v2/experiments/retina" - "github.com/yaricom/goNEAT/experiments" - "github.com/yaricom/goNEAT/neat" - "github.com/yaricom/goNEAT/neat/genetics" + "github.com/yaricom/goESHyperNEAT/v2/examples/retina" + "github.com/yaricom/goNEAT/v2/neat" + "github.com/yaricom/goNEAT/v2/neat/genetics" ) // The experiment runner boilerplate code @@ -23,23 +26,24 @@ func main() { var genomePath = flag.String("genome", "./data/retina/cppn_genome.yml", "The seed genome to start with.") var experimentName = flag.String("experiment", "retina", "The name of experiment to run. [retina]") - var logLevel = flag.Int("log-level", -1, "The logger level to be used. Overrides the one set in configuration.") + var logLevel = flag.String("log-level", "", "The logger level to be used. Overrides the one set in configuration.") var trialsCount = flag.Int("trials", 0, "The number of trials for experiment. Overrides the one set in configuration.") flag.Parse() // Seed the random-number generator with current time so that // the numbers will be different every time we run. - rand.Seed(time.Now().Unix()) + seed := time.Now().Unix() + rand.Seed(seed) // Load context configuration configFile, err := os.Open(*contextPath) if err != nil { log.Fatal("Failed to open context configuration file: ", err) } - context, err := eshyperneat.Load(configFile) + neatOptions, err := neat.LoadYAMLOptions(configFile) if err != nil { - log.Fatal("Failed to load context from config file: ", err) + log.Fatal("Failed to load NEAT options from config file: ", err) } // Load Genome @@ -83,37 +87,76 @@ func main() { // Override context configuration parameters with ones set from command line if *trialsCount > 0 { - context.NumRuns = *trialsCount + neatOptions.NumRuns = *trialsCount } - if *logLevel >= 0 { + if len(*logLevel) > 0 { neat.LogLevel = neat.LoggerLevel(*logLevel) } - // Create Retina Experiment - experiment := experiments.Experiment{ - Id: 0, - Trials: make(experiments.Trials, context.NumRuns), + // Create Experiment + experimentContext := neatOptions.NeatContext() + exp := experiment.Experiment{ + Id: 0, + Trials: make(experiment.Trials, neatOptions.NumRuns), + RandSeed: seed, } - var generationEvaluator experiments.GenerationEvaluator + var generationEvaluator experiment.GenerationEvaluator + var trialObserver experiment.TrialRunObserver switch *experimentName { case "retina": - if env, err := retina.NewRetinaEnvironment(retina.CreateRetinaDataset(), 4, context); err != nil { + opts, err := eshyperneat.LoadYAMLConfigFile(*contextPath) + if err != nil { + log.Fatal("Failed to load ES-HyperNEAT options from config file: ", err) + } else { + experimentContext = eshyperneat.NewContext(experimentContext, opts) + } + if env, err := retina.NewRetinaEnvironment(retina.CreateRetinaDataset(), 4, opts); err != nil { log.Fatalf("Failed to create retina environment, reason: %s", err) } else { - generationEvaluator = retina.NewGenerationEvaluator(*outDirPath, env) + generationEvaluator, trialObserver = retina.NewGenerationEvaluator(*outDirPath, env) } default: log.Fatalf("Unsupported experiment name requested: %s\n", *experimentName) } - fmt.Println("Done Setup, Starting Experiment!") + // prepare to execute + errChan := make(chan error) + ctx, cancel := context.WithCancel(experimentContext) + + // run experiment in the separate GO routine + go func() { + if err = exp.Execute(ctx, startGenome, generationEvaluator, trialObserver); err != nil { + errChan <- err + } else { + errChan <- nil + } + }() + + // register handler to wait for termination signals + // + go func(cancel context.CancelFunc) { + fmt.Println("\nPress Ctrl+C to stop") + + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + select { + case <-signals: + // signal to stop test fixture + cancel() + case err = <-errChan: + // stop waiting + } + }(cancel) - err = experiment.Execute(context.NeatContext, startGenome, generationEvaluator) + // Wait for experiment completion + // + err = <-errChan if err != nil { - neat.ErrorLog(fmt.Sprintf("Failed to perform %s experiment! Reason: %s\n", *experimentName, err)) - os.Exit(1) + // error during execution + log.Fatalf("Experiment execution failed: %s", err) } - // Output winner statistics - experiment.PrintStatistics() + // Print experiment results statistics + // + exp.PrintStatistics() } From 1b47db654769e35b4e2f65732b6e2d7462861a3c Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 18:57:23 +0300 Subject: [PATCH 11/57] Updated go module definition --- go.mod | 25 ++--- go.sum | 319 +++++++++------------------------------------------------ 2 files changed, 59 insertions(+), 285 deletions(-) diff --git a/go.mod b/go.mod index 5cee050..3f99201 100644 --- a/go.mod +++ b/go.mod @@ -4,28 +4,19 @@ go 1.17 require ( github.com/pkg/errors v0.9.1 - github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.7.0 github.com/yaricom/goGraphML v1.1.0 - github.com/yaricom/goNEAT v0.0.0-20210317183214-058a86c8043f + github.com/yaricom/goNEAT/v2 v2.9.3 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.4.7 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/magiconair/properties v1.8.1 // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect - github.com/pelletier/go-toml v1.2.0 // indirect + github.com/kr/pretty v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/spf13/afero v1.1.2 // indirect - github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/jwalterweatherman v1.0.0 // indirect - github.com/spf13/pflag v1.0.3 // indirect - github.com/subosito/gotenv v1.2.0 // indirect - golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 // indirect - golang.org/x/text v0.3.2 // indirect - gopkg.in/ini.v1 v1.51.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + github.com/sbinet/npyio v0.5.2 // indirect + github.com/spf13/cast v1.4.1 // indirect + golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect + gonum.org/v1/gonum v0.9.3 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index 80a934e..a697511 100644 --- a/go.sum +++ b/go.sum @@ -1,313 +1,96 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/sbinet/npyio v0.5.2 h1:phj/VX/XrekD7FKbcOwH3A4lwFcitFSkXYxS+SnmhsE= +github.com/sbinet/npyio v0.5.2/go.mod h1://79fyNB28YNRzmvn9ulBoQnsKAS8EKtdZrLqE0/1XY= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yaricom/goGraphML v1.1.0 h1:CrM6yGmZ8Azv2Id2KIzei277MPe5YFzKkOOJu45uOBM= github.com/yaricom/goGraphML v1.1.0/go.mod h1:OM0MGAy6tdufwNYPW9BS2mR6NMArD7RtlakyTs+A3Vk= -github.com/yaricom/goNEAT v0.0.0-20210317183214-058a86c8043f h1:PUjzjQbFUIiB8mO+VAH+Udg9zQqZmVbve15imziMwGg= -github.com/yaricom/goNEAT v0.0.0-20210317183214-058a86c8043f/go.mod h1:GITpkyU810UM9mzboL5bpOuod7WWqehlsrY+xZ1VdlY= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/yaricom/goNEAT/v2 v2.9.3 h1:6YfoPuLQntlvVQpdMJH+FeyBHRVhHq+mfzmhzsKinZM= +github.com/yaricom/goNEAT/v2 v2.9.3/go.mod h1:f91QbuuyIsJasv3QZZLapPZXF/WU841sJaR52nXpYTY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3 h1:DnoIG+QAMaF5NvxnGe/oKsgKcAc6PcUyl8q0VetfQ8s= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From 916bb0feb21c3f80491ad69400b3c0ab788602d6 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 19:19:27 +0300 Subject: [PATCH 12/57] Fixed error strings format to avoid linter errors. --- cppn/evolvable_substrate.go | 14 +++++++------- eshyperneat/es_hyper_neat.go | 4 ++-- hyperneat/hyper_neat.go | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index 6fa6e14..574d5bf 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -74,7 +74,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } // add input node to graph - if _, err := addNodeToBuilder(graphBuilder, in, network.InputNeuron, neatmath.NullActivation, input); err != nil { + if _, err = addNodeToBuilder(graphBuilder, in, network.InputNeuron, neatmath.NullActivation, input); err != nil { return nil, err } @@ -97,7 +97,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil targetIndex += firstHidden // adjust index to the global indexes space // add a node to the graph - if _, err := addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { + if _, err = addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { return nil, err } @@ -108,7 +108,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil // add connection if link, ok := addConnection(qp.Value, in, targetIndex); ok { // add an edge to the graph - if _, err := addEdgeToBuilder(graphBuilder, in, targetIndex, link.Weight); err != nil { + if _, err = addEdgeToBuilder(graphBuilder, in, targetIndex, link.Weight); err != nil { return nil, err } } @@ -144,7 +144,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil targetIndex += firstHidden // adjust index to the global indexes space // add a node to the graph - if _, err := addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { + if _, err = addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { return nil, err } } else { @@ -154,7 +154,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil // add connection if link, ok := addConnection(qp.Value, hi, targetIndex); ok { // add an edge to the graph - if _, err := addEdgeToBuilder(graphBuilder, hi, targetIndex, link.Weight); err != nil { + if _, err = addEdgeToBuilder(graphBuilder, hi, targetIndex, link.Weight); err != nil { return nil, err } } @@ -174,7 +174,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } // add output node to graph - if _, err := addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, es.NodesActivation, output); err != nil { + if _, err = addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, es.NodesActivation, output); err != nil { return nil, err } @@ -197,7 +197,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil // add connection if link, ok := addConnection(qp.Value, sourceIndex, oi); ok { // add an edge to the graph - if _, err := addEdgeToBuilder(graphBuilder, sourceIndex, oi, link.Weight); err != nil { + if _, err = addEdgeToBuilder(graphBuilder, sourceIndex, oi, link.Weight); err != nil { return nil, err } } diff --git a/eshyperneat/es_hyper_neat.go b/eshyperneat/es_hyper_neat.go index 6162877..f9a88c5 100644 --- a/eshyperneat/es_hyper_neat.go +++ b/eshyperneat/es_hyper_neat.go @@ -46,7 +46,7 @@ func LoadYAMLOptions(r io.Reader) (*Options, error) { // read options var opts Options if err = yaml.Unmarshal(content, &opts); err != nil { - return nil, errors.Wrap(err, "Failed to decode ES-HyperNEAT options from YAML.") + return nil, errors.Wrap(err, "failed to decode ES-HyperNEAT options from YAML") } return &opts, nil } @@ -55,7 +55,7 @@ func LoadYAMLOptions(r io.Reader) (*Options, error) { func LoadYAMLConfigFile(path string) (*Options, error) { configFile, err := os.Open(path) if err != nil { - return nil, errors.Wrap(err, "Failed to open ES-HyperNEAT configuration file.") + return nil, errors.Wrap(err, "failed to open ES-HyperNEAT configuration file") } return LoadYAMLOptions(configFile) } diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index cbdd9b6..deb387f 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -36,7 +36,7 @@ func LoadYAMLOptions(r io.Reader) (*Options, error) { // read options var opts Options if err = yaml.Unmarshal(content, &opts); err != nil { - return nil, errors.Wrap(err, "Failed to decode HyperNEAT options from YAML.") + return nil, errors.Wrap(err, "failed to decode HyperNEAT options from YAML") } return &opts, nil } @@ -45,14 +45,14 @@ func LoadYAMLOptions(r io.Reader) (*Options, error) { func LoadYAMLConfigFile(path string) (*Options, error) { configFile, err := os.Open(path) if err != nil { - return nil, errors.Wrap(err, "Failed to open HyperNEAT configuration file.") + return nil, errors.Wrap(err, "failed to open HyperNEAT configuration file") } return LoadYAMLOptions(configFile) } func (s *SubstrateActivatorType) UnmarshalYAML(value *yaml.Node) error { if activationType, err := math.NodeActivators.ActivationTypeFromName(value.Value); err != nil { - return errors.Wrap(err, "Failed to decode substrate activator function from HyperNEAT options.") + return errors.Wrap(err, "failed to decode substrate activator function from HyperNEAT options") } else { s.SubstrateActivationType = activationType } From 25c64b90a22d5ca4cd44b7eaaa9c4aa4eafa54dc Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 20:00:52 +0300 Subject: [PATCH 13/57] Fixed code duplication in evolvable_substrate.go. Added JSCPD threshold to avoid false positives --- .github/linters/.jscpd.json | 3 ++ cppn/evolvable_substrate.go | 62 +++++++++++++++++-------------------- cppn/substrate.go | 1 - 3 files changed, 32 insertions(+), 34 deletions(-) create mode 100644 .github/linters/.jscpd.json diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 0000000..9f2da79 --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,3 @@ +{ + "threshold": 20 +} \ No newline at end of file diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index 574d5bf..e95ce8e 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -87,23 +87,10 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil } // iterate over quad points and add nodes/connections for _, qp := range qPoints { - nodePoint := NewPointF(qp.X2, qp.Y2) - targetIndex := es.Layout.IndexOfHidden(nodePoint) - if targetIndex == -1 { - // add hidden node to the substrate layout - if targetIndex, err = es.Layout.AddHiddenNode(nodePoint); err != nil { - return nil, err - } - - targetIndex += firstHidden // adjust index to the global indexes space - // add a node to the graph - if _, err = addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { - return nil, err - } - - } else { - // adjust index to the global indexes space - targetIndex += firstHidden + // add hidden node to the substrate layout if needed + targetIndex, err := es.addHiddenNode(qp, firstHidden, graphBuilder) + if err != nil { + return nil, err } // add connection if link, ok := addConnection(qp.Value, in, targetIndex); ok { @@ -134,22 +121,10 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil } // iterate over quad points and add nodes/connections for _, qp := range qPoints { - nodePoint := NewPointF(qp.X2, qp.Y2) - targetIndex := es.Layout.IndexOfHidden(nodePoint) - if targetIndex == -1 { - // add hidden node to the substrate layout - if targetIndex, err = es.Layout.AddHiddenNode(nodePoint); err != nil { - return nil, err - } - - targetIndex += firstHidden // adjust index to the global indexes space - // add a node to the graph - if _, err = addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { - return nil, err - } - } else { - // adjust index to the global indexes space - targetIndex += firstHidden + // add hidden node to the substrate layout if needed + targetIndex, err := es.addHiddenNode(qp, firstHidden, graphBuilder) + if err != nil { + return nil, err } // add connection if link, ok := addConnection(qp.Value, hi, targetIndex); ok { @@ -227,6 +202,27 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return solver, nil } +func (es *EvolvableSubstrate) addHiddenNode(qp *QuadPoint, firstHidden int, graphBuilder SubstrateGraphBuilder) (targetIndex int, err error) { + nodePoint := NewPointF(qp.X2, qp.Y2) + targetIndex = es.Layout.IndexOfHidden(nodePoint) + if targetIndex == -1 { + // add hidden node to the substrate layout + if targetIndex, err = es.Layout.AddHiddenNode(nodePoint); err != nil { + return -1, err + } + + targetIndex += firstHidden // adjust index to the global indexes space + // add a node to the graph + if _, err = addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { + return -1, err + } + } else { + // adjust index to the global indexes space + targetIndex += firstHidden + } + return targetIndex, nil +} + // Divides and initialize the quadtree from provided coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b). // Returns quadtree, in which each quadnode at (x,y) stores CPPN activation level for its position. The initialized // quadtree is used in the PruningAndExtraction phase to generate the actual ANN connections. diff --git a/cppn/substrate.go b/cppn/substrate.go index 19f75e7..813406a 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -266,7 +266,6 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { // add only connections with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], in, oi, context.LinkThreshold, context.WeightRange) - } if link != nil { connections = append(connections, link) From 9e3729d8aa5c99a1123440251c214f438a1679d6 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 20:02:20 +0300 Subject: [PATCH 14/57] Updated JSCPD configuration --- .github/linters/.jscpd.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json index 9f2da79..112294e 100644 --- a/.github/linters/.jscpd.json +++ b/.github/linters/.jscpd.json @@ -1,3 +1,10 @@ { - "threshold": 20 + "threshold": 20, + "reporters": [ + "consoleFull" + ], + "ignore": [ + "**/__snapshots__/**" + ], + "absolute": true } \ No newline at end of file From 0bb00ae6912a8945e24e30575b356f22ad6bfd1b Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 20:12:51 +0300 Subject: [PATCH 15/57] Code deduplication --- cppn/substrate_test.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index cd2a6d2..2b79b34 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v2/neat/network" "testing" ) @@ -48,19 +49,7 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - signals := []float64{0.9, 5.2, 1.2, 0.6} - err = solver.LoadSensors(signals) - require.NoError(t, err, "failed to load sensors") - - res, err := solver.RecursiveSteps() - require.NoError(t, err, "failed to perform recursive activation") - require.True(t, res, "failed to relax network") - - outs := solver.ReadOutputs() - outExpected := []float64{0.6427874813512032, 0.8685335941574246} - for i, out := range outs { - assert.Equal(t, outExpected[i], out, "wrong output at: %d", i) - } + checkNetworkSolverOutputs(solver, t) } func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { @@ -144,8 +133,12 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { assert.Equal(t, 5597, len(strOut), "wrong length of marshalled string") // test outputs + checkNetworkSolverOutputs(solver, t) +} + +func checkNetworkSolverOutputs(solver network.Solver, t *testing.T) { signals := []float64{0.9, 5.2, 1.2, 0.6} - err = solver.LoadSensors(signals) + err := solver.LoadSensors(signals) require.NoError(t, err, "failed to load sensors") res, err := solver.RecursiveSteps() From 25821a265e8781db51db0040beb0b005c9fa2a26 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 20:22:15 +0300 Subject: [PATCH 16/57] Code deduplication --- cppn/substrate_test.go | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 2b79b34..5ec8c4f 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -49,7 +49,8 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - checkNetworkSolverOutputs(solver, t) + outExpected := []float64{0.6427874813512032, 0.8685335941574246} + checkNetworkSolverOutputs(solver, outExpected, t) } func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { @@ -80,19 +81,8 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - signals := []float64{0.9, 5.2, 1.2, 0.6} - err = solver.LoadSensors(signals) - require.NoError(t, err, "failed to load sensors") - - res, err := solver.RecursiveSteps() - require.NoError(t, err, "failed to perform recursive activation") - require.True(t, res, "failed to relax network") - - outs := solver.ReadOutputs() outExpected := []float64{0.5000001657646664, 0.5000003552761682} - for i, out := range outs { - assert.Equal(t, outExpected[i], out, "wrong output at: %d", i) - } + checkNetworkSolverOutputs(solver, outExpected, t) } func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { @@ -133,10 +123,11 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { assert.Equal(t, 5597, len(strOut), "wrong length of marshalled string") // test outputs - checkNetworkSolverOutputs(solver, t) + outExpected := []float64{0.6427874813512032, 0.8685335941574246} + checkNetworkSolverOutputs(solver, outExpected, t) } -func checkNetworkSolverOutputs(solver network.Solver, t *testing.T) { +func checkNetworkSolverOutputs(solver network.Solver, outExpected []float64, t *testing.T) { signals := []float64{0.9, 5.2, 1.2, 0.6} err := solver.LoadSensors(signals) require.NoError(t, err, "failed to load sensors") @@ -146,7 +137,6 @@ func checkNetworkSolverOutputs(solver network.Solver, t *testing.T) { require.True(t, res, "failed to relax network") outs := solver.ReadOutputs() - outExpected := []float64{0.6427874813512032, 0.8685335941574246} for i, out := range outs { assert.Equal(t, outExpected[i], out, "wrong output at: %d", i) } From 8a9b698e42f5fd74cfadda798a786e9ac50717c2 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 20:36:58 +0300 Subject: [PATCH 17/57] Updated super-linter configuration --- .github/workflows/super-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 42a14a1..b152f8f 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -24,7 +24,7 @@ jobs: - name: Lint Code Base uses: github/super-linter@v4 env: - VALIDATE_ALL_CODEBASE: false + VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: master FILTER_REGEX_EXCLUDE: .*README.md GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 0550168cf02316fab45cc42fa055e12f516f270e Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 20:42:20 +0300 Subject: [PATCH 18/57] Updated super-linter configuration --- .github/workflows/super-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index b152f8f..42a14a1 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -24,7 +24,7 @@ jobs: - name: Lint Code Base uses: github/super-linter@v4 env: - VALIDATE_ALL_CODEBASE: true + VALIDATE_ALL_CODEBASE: false DEFAULT_BRANCH: master FILTER_REGEX_EXCLUDE: .*README.md GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 5d8d5faebaa520534fe5b2a7ae1cd41b4b83eaa8 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 22 Apr 2022 20:57:22 +0300 Subject: [PATCH 19/57] Updated linter configuration to use golangci-lint directly --- .github/workflows/super-linter.yml | 54 +++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 42a14a1..3a458a0 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -3,28 +3,50 @@ # # You can adjust the behavior by modifying this file. # For more information, see: -# https://github.com/github/super-linter +# https://github.com/golangci/golangci-lint-action name: Lint Code Base - on: push: - branches: [ master ] + tags: + - v* + branches: + - master + - main pull_request: - branches: [ master ] +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read jobs: - run-lint: + golangci: + name: lint runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 + with: + go-version: 1.17 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 with: - # Full git history is needed to get a proper list of changed files within `super-linter` - fetch-depth: 0 + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: v1.29 + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the all caching functionality will be complete disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true - - name: Lint Code Base - uses: github/super-linter@v4 - env: - VALIDATE_ALL_CODEBASE: false - DEFAULT_BRANCH: master - FILTER_REGEX_EXCLUDE: .*README.md - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true From 339784efe0b67df594884131ba220d27d3f96101 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 6 May 2022 15:23:37 +0300 Subject: [PATCH 20/57] Updated README to include UA badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1cd9a4f..aa5ecf4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# goESHyperNEAT 🇺🇦 +# goESHyperNEAT 🇺🇦 [![Made in Ukraine](https://img.shields.io/badge/made_in-ukraine-ffd700.svg?labelColor=0057b7)](https://u24.gov.ua) [![Build Status](https://travis-ci.org/yaricom/goESHyperNEAT.svg?branch=master)](https://travis-ci.org/yaricom/goESHyperNEAT) [![GoDoc](https://godoc.org/github.com/yaricom/goESHyperNEAT?status.svg)](https://godoc.org/github.com/yaricom/goESHyperNEAT) From 6a514bbcc536f4b59a02afcbf2af8b9918d6bec0 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Sun, 5 Jun 2022 20:16:31 +0300 Subject: [PATCH 21/57] Updated to use v3 of the goNEAT library --- cppn/cppn.go | 4 +-- cppn/evolvable_substrate.go | 7 ++--- cppn/evolvable_substrate_layout.go | 3 +- cppn/evolvable_substrate_layout_test.go | 2 +- cppn/evolvable_substrate_test.go | 2 +- cppn/substrate.go | 7 ++--- cppn/substrate_graph_builder.go | 7 ++--- cppn/substrate_graph_builder_test.go | 5 ++-- cppn/substrate_layout.go | 2 +- cppn/substrate_layout_test.go | 2 +- cppn/substrate_test.go | 4 +-- eshyperneat/es_hyper_neat_test.go | 2 +- examples/retina/cppn_network_builder.go | 6 ++-- examples/retina/retina.go | 31 +++++++++++--------- executor.go | 9 +++--- go.mod | 18 ++++++------ go.sum | 38 ++++++++++++++----------- hyperneat/hyper_neat.go | 3 +- hyperneat/hyper_neat_test.go | 2 +- 19 files changed, 77 insertions(+), 77 deletions(-) diff --git a/cppn/cppn.go b/cppn/cppn.go index d971b8c..849e6e9 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -4,8 +4,8 @@ package cppn import ( "errors" - "github.com/yaricom/goNEAT/v2/neat/genetics" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/genetics" + "github.com/yaricom/goNEAT/v3/neat/network" "math" "os" ) diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index e95ce8e..e090e70 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -2,12 +2,11 @@ package cppn import ( "container/list" + "fmt" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" + neatmath "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v3/neat/network" "math" - - "fmt" - neatmath "github.com/yaricom/goNEAT/v2/neat/math" - "github.com/yaricom/goNEAT/v2/neat/network" ) // EvolvableSubstrate The evolvable substrate holds configuration of ANN produced by CPPN within hypecube where each 4-dimensional point diff --git a/cppn/evolvable_substrate_layout.go b/cppn/evolvable_substrate_layout.go index e2f2371..0f3d4f2 100644 --- a/cppn/evolvable_substrate_layout.go +++ b/cppn/evolvable_substrate_layout.go @@ -2,9 +2,8 @@ package cppn import ( "fmt" - "github.com/pkg/errors" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/network" ) // EvolvableSubstrateLayout Defines layout of neurons in the substrate diff --git a/cppn/evolvable_substrate_layout_test.go b/cppn/evolvable_substrate_layout_test.go index cd96dd4..02d09eb 100644 --- a/cppn/evolvable_substrate_layout_test.go +++ b/cppn/evolvable_substrate_layout_test.go @@ -3,7 +3,7 @@ package cppn import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/network" "testing" ) diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index b925d7c..dd87dbf 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -4,7 +4,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v3/neat/math" "testing" ) diff --git a/cppn/substrate.go b/cppn/substrate.go index 813406a..473a794 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -2,11 +2,10 @@ package cppn import ( "errors" - "math" - "github.com/yaricom/goESHyperNEAT/v2/hyperneat" - neatmath "github.com/yaricom/goNEAT/v2/neat/math" - "github.com/yaricom/goNEAT/v2/neat/network" + neatmath "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v3/neat/network" + "math" ) // Substrate represents substrate holding configuration of ANN with weights produced by CPPN. According to HyperNEAT method diff --git a/cppn/substrate_graph_builder.go b/cppn/substrate_graph_builder.go index 272c2f2..cd8a586 100644 --- a/cppn/substrate_graph_builder.go +++ b/cppn/substrate_graph_builder.go @@ -2,11 +2,10 @@ package cppn import ( "errors" - "io" - "github.com/yaricom/goGraphML/graphml" - "github.com/yaricom/goNEAT/v2/neat/math" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v3/neat/network" + "io" ) const ( diff --git a/cppn/substrate_graph_builder_test.go b/cppn/substrate_graph_builder_test.go index 296c3bb..c38da54 100644 --- a/cppn/substrate_graph_builder_test.go +++ b/cppn/substrate_graph_builder_test.go @@ -4,11 +4,10 @@ import ( "bytes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v3/neat/network" "strings" "testing" - - "github.com/yaricom/goNEAT/v2/neat/math" - "github.com/yaricom/goNEAT/v2/neat/network" ) func TestNewGraphMLBuilder(t *testing.T) { diff --git a/cppn/substrate_layout.go b/cppn/substrate_layout.go index 46d3aee..0daf446 100644 --- a/cppn/substrate_layout.go +++ b/cppn/substrate_layout.go @@ -3,7 +3,7 @@ package cppn import ( "errors" "fmt" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/network" ) // SubstrateLayout Defines layout of neurons in the substrate diff --git a/cppn/substrate_layout_test.go b/cppn/substrate_layout_test.go index 1a7b772..82020a3 100644 --- a/cppn/substrate_layout_test.go +++ b/cppn/substrate_layout_test.go @@ -2,7 +2,7 @@ package cppn import ( "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/network" "testing" ) diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 5ec8c4f..339e914 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" - "github.com/yaricom/goNEAT/v2/neat/math" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v3/neat/network" "testing" ) diff --git a/eshyperneat/es_hyper_neat_test.go b/eshyperneat/es_hyper_neat_test.go index 20d74f4..c3bd997 100644 --- a/eshyperneat/es_hyper_neat_test.go +++ b/eshyperneat/es_hyper_neat_test.go @@ -3,7 +3,7 @@ package eshyperneat import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v3/neat/math" "os" "testing" ) diff --git a/examples/retina/cppn_network_builder.go b/examples/retina/cppn_network_builder.go index 27527de..7008368 100644 --- a/examples/retina/cppn_network_builder.go +++ b/examples/retina/cppn_network_builder.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/yaricom/goESHyperNEAT/v2/cppn" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goNEAT/v2/neat/genetics" - "github.com/yaricom/goNEAT/v2/neat/math" - "github.com/yaricom/goNEAT/v2/neat/network" + "github.com/yaricom/goNEAT/v3/neat/genetics" + "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v3/neat/network" ) const debug = true diff --git a/examples/retina/retina.go b/examples/retina/retina.go index e5624c7..904fd47 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -2,14 +2,15 @@ package retina import ( + "context" "errors" "fmt" "github.com/yaricom/goESHyperNEAT/v2/cppn" - "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/v3/experiment" + "github.com/yaricom/goNEAT/v3/experiment/utils" + "github.com/yaricom/goNEAT/v3/neat" + "github.com/yaricom/goNEAT/v3/neat/genetics" + "github.com/yaricom/goNEAT/v3/neat/network" "math" "os" ) @@ -26,7 +27,7 @@ type generationEvaluator struct { env *Environment } -// NewGenerationEvaluator is to create new generations evaluator for retina experiment. +// NewGenerationEvaluator is to create new generation's evaluator for retina experiment. func NewGenerationEvaluator(outDir string, env *Environment) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { evaluator := &generationEvaluator{ outDir: outDir, @@ -51,7 +52,11 @@ func (e *generationEvaluator) EpochEvaluated(_ *experiment.Trial, _ *experiment. } // GenerationEvaluate evaluates a population of organisms and prints their performance on the retina experiment -func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population, epoch *experiment.Generation, options *neat.Options) (err error) { +func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population *genetics.Population, epoch *experiment.Generation) error { + options, ok := neat.FromContext(ctx) + if !ok { + return neat.ErrNEATOptionsNotFound + } // Evaluate each organism on a test for idx, organism := range population.Organisms { isWinner, err := e.organismEvaluate(organism) @@ -60,12 +65,12 @@ func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population } neat.InfoLog(fmt.Sprintf("organism #%d fitness %f \n", idx, organism.Fitness)) - if isWinner && (epoch.Best == nil || organism.Fitness > epoch.Best.Fitness) { + if isWinner && (epoch.Champion == nil || organism.Fitness > epoch.Champion.Fitness) { epoch.Solved = true epoch.WinnerNodes = len(organism.Genotype.Nodes) epoch.WinnerGenes = organism.Genotype.Extrons() epoch.WinnerEvals = options.PopSize*epoch.Id + organism.Genotype.Id - epoch.Best = organism + epoch.Champion = organism if epoch.WinnerNodes == 9 { // You could dump out optimal genomes here if desired if optPath, err := utils.WriteGenomePlain("xor_optimal", e.outDir, organism, epoch); err != nil { @@ -80,9 +85,9 @@ func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population // Fill statistics about current epoch epoch.FillPopulationStatistics(population) - // Only print to file every print_every generations + // Only print to file every print_every generation if epoch.Solved || epoch.Id%options.PrintEvery == 0 { - if _, err = utils.WritePopulationPlain(e.outDir, population, epoch); err != nil { + if _, err := utils.WritePopulationPlain(e.outDir, population, epoch); err != nil { neat.ErrorLog(fmt.Sprintf("Failed to dump population, reason: %s\n", err)) return err } @@ -90,7 +95,7 @@ func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population if epoch.Solved { // print winner organism - org := epoch.Best + org := epoch.Champion if depth, err := org.Phenotype.MaxActivationDepthFast(0); err == nil { neat.InfoLog(fmt.Sprintf("Activation depth of the winner: %d\n", depth)) } @@ -118,7 +123,7 @@ func (e *generationEvaluator) GenerationEvaluate(population *genetics.Population neat.InfoLog(fmt.Sprintf("Generation #%d winner's substrate dumped to: %s\n", epoch.Id, substrPath)) } } - return err + return nil } // organismEvaluate evaluates an individual phenotype network with retina experiment and returns true if its won diff --git a/executor.go b/executor.go index 8df7862..3da0f02 100644 --- a/executor.go +++ b/executor.go @@ -5,7 +5,10 @@ import ( "flag" "fmt" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goNEAT/v2/experiment" + "github.com/yaricom/goESHyperNEAT/v2/examples/retina" + "github.com/yaricom/goNEAT/v3/experiment" + "github.com/yaricom/goNEAT/v3/neat" + "github.com/yaricom/goNEAT/v3/neat/genetics" "log" "math/rand" "os" @@ -13,10 +16,6 @@ import ( "strings" "syscall" "time" - - "github.com/yaricom/goESHyperNEAT/v2/examples/retina" - "github.com/yaricom/goNEAT/v2/neat" - "github.com/yaricom/goNEAT/v2/neat/genetics" ) // The experiment runner boilerplate code diff --git a/go.mod b/go.mod index 3f99201..99c0017 100644 --- a/go.mod +++ b/go.mod @@ -4,19 +4,17 @@ go 1.17 require ( github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/yaricom/goGraphML v1.1.0 - github.com/yaricom/goNEAT/v2 v2.9.3 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + github.com/yaricom/goNEAT/v3 v3.0.1 + gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/pretty v0.1.0 // indirect + github.com/davecgh/go-spew v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sbinet/npyio v0.5.2 // indirect - github.com/spf13/cast v1.4.1 // indirect - golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect - gonum.org/v1/gonum v0.9.3 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + github.com/sbinet/npyio v0.6.0 // indirect + github.com/spf13/cast v1.5.0 // indirect + golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 // indirect + gonum.org/v1/gonum v0.11.0 // indirect ) diff --git a/go.sum b/go.sum index a697511..edde1cd 100644 --- a/go.sum +++ b/go.sum @@ -4,11 +4,13 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -16,13 +18,11 @@ github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmn github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -30,26 +30,29 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/sbinet/npyio v0.5.2 h1:phj/VX/XrekD7FKbcOwH3A4lwFcitFSkXYxS+SnmhsE= -github.com/sbinet/npyio v0.5.2/go.mod h1://79fyNB28YNRzmvn9ulBoQnsKAS8EKtdZrLqE0/1XY= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/sbinet/npyio v0.6.0 h1:IyqqQIzRjDym9xnIXsToCKei/qCzxDP+Y74KoMlMgXo= +github.com/sbinet/npyio v0.6.0/go.mod h1:/q3BNr6dJOy+t6h7RZchTJ0nwRJO52mivaem29WE1j8= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yaricom/goGraphML v1.1.0 h1:CrM6yGmZ8Azv2Id2KIzei277MPe5YFzKkOOJu45uOBM= github.com/yaricom/goGraphML v1.1.0/go.mod h1:OM0MGAy6tdufwNYPW9BS2mR6NMArD7RtlakyTs+A3Vk= -github.com/yaricom/goNEAT/v2 v2.9.3 h1:6YfoPuLQntlvVQpdMJH+FeyBHRVhHq+mfzmhzsKinZM= -github.com/yaricom/goNEAT/v2 v2.9.3/go.mod h1:f91QbuuyIsJasv3QZZLapPZXF/WU841sJaR52nXpYTY= +github.com/yaricom/goNEAT/v3 v3.0.1 h1:C5gYl2ErevPFDl5gcbav3JhFRADKCkiWEa4jUM/y9hA= +github.com/yaricom/goNEAT/v3 v3.0.1/go.mod h1:gkZxHLDW3dRwacNk0ITgt9okkCSnFeTbosWfLFnYucg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= @@ -79,11 +82,12 @@ golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3 h1:DnoIG+QAMaF5NvxnGe/oKsgKcAc6PcUyl8q0VetfQ8s= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= +gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= @@ -91,6 +95,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index deb387f..93d2a9f 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -3,12 +3,11 @@ package hyperneat import ( "github.com/pkg/errors" + "github.com/yaricom/goNEAT/v3/neat/math" "gopkg.in/yaml.v3" "io" "io/ioutil" "os" - - "github.com/yaricom/goNEAT/v2/neat/math" ) type SubstrateActivatorType struct { diff --git a/hyperneat/hyper_neat_test.go b/hyperneat/hyper_neat_test.go index 8c8afdb..456f1bf 100644 --- a/hyperneat/hyper_neat_test.go +++ b/hyperneat/hyper_neat_test.go @@ -3,7 +3,7 @@ package hyperneat import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v2/neat/math" + "github.com/yaricom/goNEAT/v3/neat/math" "os" "testing" ) From 4ea9c370177a977fdb67dafd01a07648db013a27 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Sun, 5 Jun 2022 20:26:55 +0300 Subject: [PATCH 22/57] Updated experiment executor to use utilities to read NEAT options and seed genome. --- executor.go | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/executor.go b/executor.go index 3da0f02..d8299a4 100644 --- a/executor.go +++ b/executor.go @@ -13,7 +13,6 @@ import ( "math/rand" "os" "os/signal" - "strings" "syscall" "time" ) @@ -36,32 +35,20 @@ func main() { rand.Seed(seed) // Load context configuration - configFile, err := os.Open(*contextPath) + neatOptions, err := neat.ReadNeatOptionsFromFile(*contextPath) if err != nil { - log.Fatal("Failed to open context configuration file: ", err) - } - neatOptions, err := neat.LoadYAMLOptions(configFile) - if err != nil { - log.Fatal("Failed to load NEAT options from config file: ", err) + log.Fatal("Failed to load NEAT options: ", err) } // Load Genome log.Printf("Loading start genome for %s experiment\n", *experimentName) - genomeFile, err := os.Open(*genomePath) - if err != nil { - log.Fatal("Failed to open genome file: ", err) - } - encoding := genetics.PlainGenomeEncoding - if strings.HasSuffix(*genomePath, ".yml") { - encoding = genetics.YAMLGenomeEncoding - } - decoder, err := genetics.NewGenomeReader(genomeFile, encoding) + reader, err := genetics.NewGenomeReaderFromFile(*genomePath) if err != nil { - log.Fatal("Failed to create genome decoder: ", err) + log.Fatalf("Failed to open genome file, reason: '%s'", err) } - startGenome, err := decoder.Read() + startGenome, err := reader.Read() if err != nil { - log.Fatal("Failed to read start genome of CPPN: ", err) + log.Fatalf("Failed to read start genome, reason: '%s'", err) } fmt.Println(startGenome) @@ -158,4 +145,22 @@ func main() { // Print experiment results statistics // exp.PrintStatistics() + + // Save experiment data in native format + // + expResPath := fmt.Sprintf("%s/%s.dat", outDir, *experimentName) + if expResFile, err := os.Create(expResPath); err != nil { + log.Fatal("Failed to create file for experiment results", err) + } else if err = exp.Write(expResFile); err != nil { + log.Fatal("Failed to save experiment results", err) + } + + // Save experiment data in Numpy NPZ format if requested + // + npzResPath := fmt.Sprintf("%s/%s.npz", outDir, *experimentName) + if npzResFile, err := os.Create(npzResPath); err != nil { + log.Fatalf("Failed to create file for experiment results: [%s], reason: %s", npzResPath, err) + } else if err = exp.WriteNPZ(npzResFile); err != nil { + log.Fatal("Failed to save experiment results as NPZ file", err) + } } From 6e8208fc89396ce835c1e5a270c40d3cc02f57fe Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Sun, 5 Jun 2022 20:36:52 +0300 Subject: [PATCH 23/57] Updated experiment executor and retina experiment sample to get ES-HyperNEAT options from experiment context. --- eshyperneat/context.go | 7 ++++++- examples/retina/environment.go | 6 ++---- examples/retina/retina.go | 13 +++++++++---- executor.go | 4 ++-- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/eshyperneat/context.go b/eshyperneat/context.go index f0c4288..b466278 100644 --- a/eshyperneat/context.go +++ b/eshyperneat/context.go @@ -1,6 +1,11 @@ package eshyperneat -import "context" +import ( + "context" + "errors" +) + +var ErrESHyperNEATOptionsNotFound = errors.New("ES-HyperNEAT options not found in the context") // key is an unexported type for keys defined in this package. // This prevents collisions with keys defined in other packages. diff --git a/examples/retina/environment.go b/examples/retina/environment.go index bb4783a..b61c4bf 100644 --- a/examples/retina/environment.go +++ b/examples/retina/environment.go @@ -3,7 +3,6 @@ package retina import ( "fmt" "github.com/pkg/errors" - "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "strings" ) @@ -24,12 +23,11 @@ func (s DetectionSide) String() string { type Environment struct { visualObjects []VisualObject inputSize int - context *eshyperneat.Options } // NewRetinaEnvironment creates a new Retina Environment with a dataset of all possible Visual Object with specified // number of inputs to be acquired from provided objects -func NewRetinaEnvironment(dataSet []VisualObject, inputSize int, context *eshyperneat.Options) (*Environment, error) { +func NewRetinaEnvironment(dataSet []VisualObject, inputSize int) (*Environment, error) { // check that provided visual objects has data points equal to the inputSize for _, o := range dataSet { if len(o.data) != inputSize { @@ -38,7 +36,7 @@ func NewRetinaEnvironment(dataSet []VisualObject, inputSize int, context *eshype inputSize, len(o.data), o) } } - return &Environment{visualObjects: dataSet, inputSize: inputSize, context: context}, nil + return &Environment{visualObjects: dataSet, inputSize: inputSize}, nil } // VisualObject represents a left, right, or both, object classified by retina diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 904fd47..908f79f 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "github.com/yaricom/goESHyperNEAT/v2/cppn" + "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "github.com/yaricom/goNEAT/v3/experiment" "github.com/yaricom/goNEAT/v3/experiment/utils" "github.com/yaricom/goNEAT/v3/neat" @@ -59,7 +60,7 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population } // Evaluate each organism on a test for idx, organism := range population.Organisms { - isWinner, err := e.organismEvaluate(organism) + isWinner, err := e.organismEvaluate(ctx, organism) if err != nil { return err } @@ -127,7 +128,11 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population } // organismEvaluate evaluates an individual phenotype network with retina experiment and returns true if its won -func (e generationEvaluator) organismEvaluate(organism *genetics.Organism) (bool, error) { +func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *genetics.Organism) (bool, error) { + options, ok := eshyperneat.FromContext(ctx) + if !ok { + return false, eshyperneat.ErrESHyperNEATOptionsNotFound + } // get CPPN network solver cppnSolver, err := organism.Phenotype.FastNetworkSolver() if err != nil { @@ -141,9 +146,9 @@ func (e generationEvaluator) organismEvaluate(organism *genetics.Organism) (bool return false, err } // create ES-HyperNEAT solver - substr := cppn.NewEvolvableSubstrate(layout, e.env.context.SubstrateActivator.SubstrateActivationType) + substr := cppn.NewEvolvableSubstrate(layout, options.SubstrateActivator.SubstrateActivationType) graph := cppn.NewSubstrateGraphMLBuilder("retina ES-HyperNEAT", false) - solver, err := substr.CreateNetworkSolver(cppnSolver, graph, e.env.context) + solver, err := substr.CreateNetworkSolver(cppnSolver, graph, options) if err != nil { return false, err } diff --git a/executor.go b/executor.go index d8299a4..af71f38 100644 --- a/executor.go +++ b/executor.go @@ -56,7 +56,7 @@ func main() { // Check if output dir exists outDir := *outDirPath - if _, err := os.Stat(outDir); err == nil { + if _, err = os.Stat(outDir); err == nil { // backup it backUpDir := fmt.Sprintf("%s-%s", outDir, time.Now().Format("2006-01-02T15_04_05")) // clear it @@ -96,7 +96,7 @@ func main() { } else { experimentContext = eshyperneat.NewContext(experimentContext, opts) } - if env, err := retina.NewRetinaEnvironment(retina.CreateRetinaDataset(), 4, opts); err != nil { + if env, err := retina.NewRetinaEnvironment(retina.CreateRetinaDataset(), 4); err != nil { log.Fatalf("Failed to create retina environment, reason: %s", err) } else { generationEvaluator, trialObserver = retina.NewGenerationEvaluator(*outDirPath, env) From 51cca8ed23af4752f342c2680991a2865fee0885 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Sun, 5 Jun 2022 22:21:02 +0300 Subject: [PATCH 24/57] Implemented support for soft stabilization of number of species in population --- examples/common.go | 24 +++++++++ examples/retina/cppn_network_builder.go | 66 ------------------------- examples/retina/environment.go | 13 +++-- examples/retina/retina.go | 56 +++++++++++++++------ executor.go | 6 ++- 5 files changed, 78 insertions(+), 87 deletions(-) create mode 100644 examples/common.go delete mode 100644 examples/retina/cppn_network_builder.go diff --git a/examples/common.go b/examples/common.go new file mode 100644 index 0000000..5d08194 --- /dev/null +++ b/examples/common.go @@ -0,0 +1,24 @@ +package examples + +import "github.com/yaricom/goNEAT/v3/neat" + +const ( + compatibilityThresholdStep = 0.1 + compatibilityThresholdMinValue = 0.3 +) + +// AdjustSpeciesNumber is to adjust species count by keeping it constant +func AdjustSpeciesNumber(speciesCount, epochId, adjustFrequency, numberSpeciesTarget int, options *neat.Options) { + if epochId%adjustFrequency == 0 { + if speciesCount < numberSpeciesTarget { + options.CompatThreshold -= compatibilityThresholdStep + } else if speciesCount > numberSpeciesTarget { + options.CompatThreshold += compatibilityThresholdStep + } + + // to avoid dropping too low + if options.CompatThreshold < compatibilityThresholdMinValue { + options.CompatThreshold = compatibilityThresholdMinValue + } + } +} diff --git a/examples/retina/cppn_network_builder.go b/examples/retina/cppn_network_builder.go deleted file mode 100644 index 7008368..0000000 --- a/examples/retina/cppn_network_builder.go +++ /dev/null @@ -1,66 +0,0 @@ -package retina - -import ( - "fmt" - "github.com/yaricom/goESHyperNEAT/v2/cppn" - "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goNEAT/v3/neat/genetics" - "github.com/yaricom/goNEAT/v3/neat/math" - "github.com/yaricom/goNEAT/v3/neat/network" -) - -const debug = true - -// CPPNNetworkBuilder handles the building of Network's using CPPN network's to query connections/topology by the esHyperNEAT algorithm -type CPPNNetworkBuilder struct { - UseLEO bool - SubstrateLayout cppn.EvolvableSubstrateLayout - NodesActivation math.NodeActivationType - Context *eshyperneat.Options - - // New Graph Builder generator function. - // Note: Graph Builder is used to store graphs in a user-friendly format - NewGraphBuilder func() *cppn.SubstrateGraphBuilder -} - -// CreateANNFromCPPNOrganism creates a NetworkSolver (ANN) by querying the Organism.Phenotype cppnNetwork by the ESHyperNeat Algorithm -func (builder *CPPNNetworkBuilder) CreateANNFromCPPNOrganism(cppnOrganism *genetics.Organism) (network.Solver, error) { - return builder.CreateANNFromCPPN(cppnOrganism.Phenotype) -} - -// CreateANNFromCPPN creates a NetworkSolver (ANN) by querying the cppnNetwork by the ESHyperNeat Algorithm -func (builder *CPPNNetworkBuilder) CreateANNFromCPPN(cppnNetwork *network.Network) (network.Solver, error) { - // create substrate which will be connected to form the network - substrate := cppn.NewEvolvableSubstrate(builder.SubstrateLayout, builder.NodesActivation) - cppnFastNetwork, err := cppnNetwork.FastNetworkSolver() - if err != nil { - return nil, err - } - - // create a new graphBuilder to store the graph in a graphable and user-friendly format - graphml xml - graphBuilder := builder.NewGraphBuilder() - - // unwrap graphBuilder. graphBuilder may not be provided (its nil) - var annSolver network.Solver - if graphBuilder == nil { - // create our ann by querying the cppn network and applying the eshypernet quadtree/pruning/banding algorithm - annSolver, err = substrate.CreateNetworkSolver( - cppnFastNetwork, nil, builder.Context, - ) - } else { - // create our ann by querying the cppn network and applying the eshypernet quadtree/pruning/banding algorithm - annSolver, err = substrate.CreateNetworkSolver( - cppnFastNetwork, *graphBuilder, builder.Context, - ) - } - if err != nil { - return nil, err - } - - if debug { - fmt.Print("CPPN: ", cppnFastNetwork, "\n") - fmt.Print("Generated ANN: ", annSolver, "\n\n") - } - - return annSolver, nil -} diff --git a/examples/retina/environment.go b/examples/retina/environment.go index b61c4bf..24bdb0a 100644 --- a/examples/retina/environment.go +++ b/examples/retina/environment.go @@ -21,12 +21,14 @@ func (s DetectionSide) String() string { // Environment holds the dataset and evaluation methods for the modular retina experiment type Environment struct { + // the data set of visual objects to be detected visualObjects []VisualObject - inputSize int + // the size of input data array + inputSize int } // NewRetinaEnvironment creates a new Retina Environment with a dataset of all possible Visual Object with specified -// number of inputs to be acquired from provided objects +// number of inputs to be acquired from provided objects. func NewRetinaEnvironment(dataSet []VisualObject, inputSize int) (*Environment, error) { // check that provided visual objects has data points equal to the inputSize for _, o := range dataSet { @@ -36,7 +38,10 @@ func NewRetinaEnvironment(dataSet []VisualObject, inputSize int) (*Environment, inputSize, len(o.data), o) } } - return &Environment{visualObjects: dataSet, inputSize: inputSize}, nil + return &Environment{ + visualObjects: dataSet, + inputSize: inputSize, + }, nil } // VisualObject represents a left, right, or both, object classified by retina @@ -63,7 +68,7 @@ func (o *VisualObject) String() string { } // parseVisualObjectConfig parses config semantically in the format -// (config = "x1 x2 \n x3 x4") to [ x1, x2, x3, x4 ] where if xi == "o" => xi = 1 +// (config = "x1 x2 \n x3 x4") to [ xf1, xf2, xf3, xf4 ] where if xi == "o" => xfi = 1.0 func parseVisualObjectConfig(config string) []float64 { data := make([]float64, 0) lines := strings.Split(config, "\n") diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 908f79f..3d801e9 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/yaricom/goESHyperNEAT/v2/cppn" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" + "github.com/yaricom/goESHyperNEAT/v2/examples" "github.com/yaricom/goNEAT/v3/experiment" "github.com/yaricom/goNEAT/v3/experiment/utils" "github.com/yaricom/goNEAT/v3/neat" @@ -21,18 +22,29 @@ const ( maxFitness = 1000.0 // fitnessThreshold is the fitness value for which an organism is considered to have won the experiment fitnessThreshold = maxFitness + + debug = false ) type generationEvaluator struct { outDir string env *Environment + + // The target number of species to be maintained + numSpeciesTarget int + // The species compatibility threshold adjustment frequency + compatAdjustFreq int } -// NewGenerationEvaluator is to create new generation's evaluator for retina experiment. -func NewGenerationEvaluator(outDir string, env *Environment) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { +// NewGenerationEvaluator is to create new generation's evaluator for retina experiment. The numSpeciesTarget specifies the +// target number of species to maintain in the population. If the number of species differ from the numSpeciesTarget it +// will be automatically adjusted with compatAdjustFreq frequency, i.e., at each epoch % compatAdjustFreq == 0 +func NewGenerationEvaluator(outDir string, env *Environment, numSpeciesTarget, compatAdjustFreq int) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { evaluator := &generationEvaluator{ - outDir: outDir, - env: env, + outDir: outDir, + env: env, + numSpeciesTarget: numSpeciesTarget, + compatAdjustFreq: compatAdjustFreq, } return evaluator, evaluator } @@ -59,12 +71,16 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population return neat.ErrNEATOptionsNotFound } // Evaluate each organism on a test - for idx, organism := range population.Organisms { + maxPopulationFitness := 0.0 + for _, organism := range population.Organisms { isWinner, err := e.organismEvaluate(ctx, organism) if err != nil { return err } - neat.InfoLog(fmt.Sprintf("organism #%d fitness %f \n", idx, organism.Fitness)) + + if organism.Fitness > maxPopulationFitness { + maxPopulationFitness = organism.Fitness + } if isWinner && (epoch.Champion == nil || organism.Fitness > epoch.Champion.Fitness) { epoch.Solved = true @@ -123,6 +139,14 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population } else { neat.InfoLog(fmt.Sprintf("Generation #%d winner's substrate dumped to: %s\n", epoch.Id, substrPath)) } + } else if epoch.Id < options.NumGenerations-1 { + speciesCount := len(population.Species) + + // adjust species count by keeping it constant + examples.AdjustSpeciesNumber(speciesCount, epoch.Id, e.compatAdjustFreq, e.numSpeciesTarget, options) + + neat.InfoLog(fmt.Sprintf("%d species -> %d organisms [compatibility threshold: %.1f, target: %d], max fitness of population: %.2f\n", + speciesCount, len(population.Organisms), options.CompatThreshold, e.numSpeciesTarget, maxPopulationFitness)) } return nil } @@ -181,15 +205,8 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen fitness := maxFitness / (1.0 + errorSum) avgError := errorSum / count - neat.InfoLog(fmt.Sprintf("Average error: %f, errors sum: %f, false detections: %f from: %f", - avgError, errorSum, detectionErrorCount, count)) - nodes, _ := graph.NodesCount() - edges, _ := graph.EdgesCount() - neat.InfoLog(fmt.Sprintf("Substrate: #nodes = %d, #edges = %d | CPPN phenotype: #nodes = %d, #edges = %d", - nodes, edges, cppnSolver.NodeCount(), cppnSolver.LinkCount())) - isWinner := false - if organism.Fitness > fitnessThreshold { + if fitness >= fitnessThreshold { isWinner = true fmt.Printf("Found a Winner! \n") // save solver graph to the winner organism @@ -200,7 +217,16 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen organism.Error = avgError organism.Fitness = fitness - return organism.IsWinner, nil + if debug { + neat.InfoLog(fmt.Sprintf("Average error: %f, errors sum: %f, false detections: %f from: %f", + avgError, errorSum, detectionErrorCount, count)) + nodes, _ := graph.NodesCount() + edges, _ := graph.EdgesCount() + neat.InfoLog(fmt.Sprintf("Substrate: #nodes = %d, #edges = %d | CPPN phenotype: #nodes = %d, #edges = %d", + nodes, edges, cppnSolver.NodeCount(), cppnSolver.LinkCount())) + } + + return isWinner, nil } // evaluateNetwork is to evaluate provided network solver using provided visual objects to test prediction performance. diff --git a/executor.go b/executor.go index af71f38..957ae40 100644 --- a/executor.go +++ b/executor.go @@ -17,12 +17,14 @@ import ( "time" ) -// The experiment runner boilerplate code +// The experiment runner code func main() { var outDirPath = flag.String("out", "./out", "The output directory to store results.") var contextPath = flag.String("context", "./data/retina/es_hyper.neat.yml", "The execution context configuration file.") var genomePath = flag.String("genome", "./data/retina/cppn_genome.yml", "The seed genome to start with.") var experimentName = flag.String("experiment", "retina", "The name of experiment to run. [retina]") + var speciesTarget = flag.Int("species_target", 15, "The target number of species to maintain.") + var speciesCompatAdjustFreq = flag.Int("species_adjust_freq", 10, "The frequency of species compatibility threshold adjustments when trying to maintain their number.") var logLevel = flag.String("log-level", "", "The logger level to be used. Overrides the one set in configuration.") var trialsCount = flag.Int("trials", 0, "The number of trials for experiment. Overrides the one set in configuration.") @@ -99,7 +101,7 @@ func main() { if env, err := retina.NewRetinaEnvironment(retina.CreateRetinaDataset(), 4); err != nil { log.Fatalf("Failed to create retina environment, reason: %s", err) } else { - generationEvaluator, trialObserver = retina.NewGenerationEvaluator(*outDirPath, env) + generationEvaluator, trialObserver = retina.NewGenerationEvaluator(*outDirPath, env, *speciesTarget, *speciesCompatAdjustFreq) } default: log.Fatalf("Unsupported experiment name requested: %s\n", *experimentName) From c61db95d0801280cc28906930ed446ca003bd3e7 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Sun, 5 Jun 2022 22:27:30 +0300 Subject: [PATCH 25/57] Updated retina configuration --- data/retina/es_hyper.neat.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/data/retina/es_hyper.neat.yml b/data/retina/es_hyper.neat.yml index e6b8504..9563fba 100644 --- a/data/retina/es_hyper.neat.yml +++ b/data/retina/es_hyper.neat.yml @@ -18,9 +18,9 @@ excess_coeff: 1.0 mutdiff_coeff: 0.4 # This global tells compatibility threshold under which two Genomes are considered the same species -compat_threshold: 3.0 +compat_threshold: 4.0 # How much does age matter? Gives a fitness boost up to some young age (niche). If it is 1, then young species get no fitness boost. -age_significance: 1.0 +age_significance: 1.1 # Percent of average fitness for survival, how many get to reproduce based on survival_thresh * pop_size survival_thresh: 0.2 @@ -41,7 +41,7 @@ mutate_gene_reenable_prob: 0.0 # Probability of adding new node mutate_add_node_prob: 0.03 # Probability of adding new link between nodes -mutate_add_link_prob: 0.08 +mutate_add_link_prob: 0.1 # Probability of making connections from disconnected sensors (input, bias type neurons) mutate_connect_sensors: 0.5 @@ -92,10 +92,12 @@ log_level: info # The nodes activation functions list to choose from (activation function -> it's selection probability) node_activators: - - SigmoidBipolarActivation 0.25 - - GaussianBipolarActivation 0.35 + - SigmoidBipolarActivation 0.15 + - SigmoidSteepenedActivation 0.15 + - GaussianBipolarActivation 0.15 - LinearAbsActivation 0.15 - - SineActivation 0.25 + - SineActivation 0.15 + - TanhActivation 0.25 ######################################## # The HyperNEAT specific configuration # From 5078887bcf5776db2da3989a915497a9f1504e89 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Tue, 7 Jun 2022 19:03:57 +0300 Subject: [PATCH 26/57] Fixed bug in predictions evaluation. Added solver flushing. --- examples/retina/retina.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 3d801e9..1dc3db8 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -3,8 +3,8 @@ package retina import ( "context" - "errors" "fmt" + "github.com/pkg/errors" "github.com/yaricom/goESHyperNEAT/v2/cppn" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "github.com/yaricom/goESHyperNEAT/v2/examples" @@ -220,10 +220,8 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen if debug { neat.InfoLog(fmt.Sprintf("Average error: %f, errors sum: %f, false detections: %f from: %f", avgError, errorSum, detectionErrorCount, count)) - nodes, _ := graph.NodesCount() - edges, _ := graph.EdgesCount() neat.InfoLog(fmt.Sprintf("Substrate: #nodes = %d, #edges = %d | CPPN phenotype: #nodes = %d, #edges = %d", - nodes, edges, cppnSolver.NodeCount(), cppnSolver.LinkCount())) + solver.NodeCount(), solver.LinkCount(), cppnSolver.NodeCount(), cppnSolver.LinkCount())) } return isWinner, nil @@ -232,6 +230,11 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen // evaluateNetwork is to evaluate provided network solver using provided visual objects to test prediction performance. // Returns the prediction loss value or error if failed to evaluate. func evaluateNetwork(solver network.Solver, leftObj VisualObject, rightObj VisualObject) (float64, error) { + // flush current network state + if _, err := solver.Flush(); err != nil { + return -1, err + } + // Create input by joining data from left and right visual objects inputs := append(leftObj.data, rightObj.data...) @@ -240,6 +243,8 @@ func evaluateNetwork(solver network.Solver, leftObj VisualObject, rightObj Visua if err := solver.LoadSensors(inputs); err != nil { return loss, err } + + // Propagate activation if relaxed, err := solver.RecursiveSteps(); err != nil { return loss, err } else if !relaxed { @@ -257,10 +262,10 @@ func evaluatePredictions(predictions []float64, leftObj VisualObject, rightObj V // Convert predictions[i] to 1.0 or 0.0 about 0.5 threshold normPredictions := make([]float64, len(predictions)) for i := 0; i < len(normPredictions); i++ { - if normPredictions[i] >= 0.5 { + if predictions[i] >= 0.5 { normPredictions[i] = 1.0 } else { - normPredictions[i] = 0.0 + predictions[i] = 0.0 } } From b12b8a06fc67962e45b1ab4d02e80631805e5b57 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Tue, 21 Jun 2022 20:00:57 +0300 Subject: [PATCH 27/57] Fixed bug with ineffectual assigment to err --- cppn/substrate_graph_builder.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cppn/substrate_graph_builder.go b/cppn/substrate_graph_builder.go index cd8a586..179badd 100644 --- a/cppn/substrate_graph_builder.go +++ b/cppn/substrate_graph_builder.go @@ -83,7 +83,7 @@ func (b *graphMLBuilder) AddNode(nodeId int, nodeNeuronType network.NodeNeuronTy return nil } -func (b *graphMLBuilder) AddWeightedEdge(sourceId, targetId int, weight float64) (err error) { +func (b *graphMLBuilder) AddWeightedEdge(sourceId, targetId int, weight float64) error { // create attributes map edgeAttr := make(map[string]interface{}) edgeAttr[edgeAttrWeight] = weight @@ -102,10 +102,10 @@ func (b *graphMLBuilder) AddWeightedEdge(sourceId, targetId int, weight float64) if graph, err := b.graph(); err != nil { return err - } else { - _, err = graph.AddEdge(source, target, edgeAttr, graphml.EdgeDirectionDefault, "") + } else if _, err = graph.AddEdge(source, target, edgeAttr, graphml.EdgeDirectionDefault, ""); err != nil { + return err } - return err + return nil } func (b *graphMLBuilder) NodesCount() (int, error) { From adf78e4ddf58b7069ea5b0acb063d483947ab21b Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 15:05:57 +0300 Subject: [PATCH 28/57] Renamed parameters and variables for better clarity. --- cppn/cppn.go | 16 +++++------ cppn/evolvable_substrate.go | 56 ++++++++++++++++++------------------- cppn/substrate.go | 33 +++++++++++----------- 3 files changed, 51 insertions(+), 54 deletions(-) diff --git a/cppn/cppn.go b/cppn/cppn.go index 849e6e9..16d1f33 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -84,18 +84,18 @@ func nodeVariance(node *QuadNode) float64 { cppnValues := nodeCPPNValues(node) // calculate median and variance - m, v := 0.0, 0.0 - for _, f := range cppnValues { - m += f + meanW, variance := 0.0, 0.0 + for _, w := range cppnValues { + meanW += w } - m /= float64(len(cppnValues)) + meanW /= float64(len(cppnValues)) - for _, f := range cppnValues { - v += math.Pow(f-m, 2) + for _, w := range cppnValues { + variance += math.Pow(w-meanW, 2) } - v /= float64(len(cppnValues)) + variance /= float64(len(cppnValues)) - return v + return variance } // Collects the CPPN values stored in a given quadtree node diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index e090e70..fcdab07 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -38,8 +38,7 @@ func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation neat // CreateNetworkSolver Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which // used to define connections between network nodes. Optional graph_builder can be provided to collect graph nodes and edges // of created network solver. With graph builder it is possible to save/load network configuration as well as visualize it. -func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuilder SubstrateGraphBuilder, - context *eshyperneat.Options) (network.Solver, error) { +func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuilder SubstrateGraphBuilder, context *eshyperneat.Options) (network.Solver, error) { es.cppn = cppn // the network layers will be collected in order: bias, input, output, hidden @@ -225,8 +224,7 @@ func (es *EvolvableSubstrate) addHiddenNode(qp *QuadPoint, firstHidden int, grap // Divides and initialize the quadtree from provided coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b). // Returns quadtree, in which each quadnode at (x,y) stores CPPN activation level for its position. The initialized // quadtree is used in the PruningAndExtraction phase to generate the actual ANN connections. -func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, - context *eshyperneat.Options) (root *QuadNode, err error) { +func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, options *eshyperneat.Options) (root *QuadNode, err error) { root = NewQuadNode(0.0, 0.0, 1.0, 1) queue := list.New() @@ -260,7 +258,7 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, } // Divide until initial resolution or if variance is still high - if p.Level < context.InitialDepth || (p.Level < context.MaximalDepth && nodeVariance(p) > context.DivisionThreshold) { + if p.Level < options.InitialDepth || (p.Level < options.MaximalDepth && nodeVariance(p) > options.DivisionThreshold) { for _, c := range p.Nodes { queue.PushBack(c) } @@ -274,7 +272,7 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, // Receive coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b) and initialized quadtree node. // Adds the connections that are in bands of the two-dimensional cross-section of the hypercube containing the source // or target node to the connections list and return modified list. -func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadPoint, node *QuadNode, outgoing bool, context *eshyperneat.Options) ([]*QuadPoint, error) { +func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadPoint, node *QuadNode, outgoing bool, options *eshyperneat.Options) ([]*QuadPoint, error) { // fast check if len(node.Nodes) == 0 { return connections, nil @@ -283,11 +281,11 @@ func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadP // Traverse quadtree depth-first until the current node’s variance is smaller than the variance threshold or // until the node has no children (which means that the variance is zero). left, right, top, bottom := 0.0, 0.0, 0.0, 0.0 - for _, c := range node.Nodes { - childVariance := nodeVariance(c) + for _, quadNode := range node.Nodes { + childVariance := nodeVariance(quadNode) - if childVariance >= context.VarianceThreshold { - if conn, err := es.pruneAndExpress(a, b, connections, c, outgoing, context); err != nil { + if childVariance >= options.VarianceThreshold { + if conn, err := es.pruneAndExpress(a, b, connections, quadNode, outgoing, options); err != nil { return nil, err } else { connections = append(connections, conn...) @@ -295,56 +293,56 @@ func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadP } else { // Determine if point is in a band by checking neighbor CPPN values if outgoing { - if l, err := es.queryCPPN(a, b, c.X-node.Width, c.Y); err != nil { + if l, err := es.queryCPPN(a, b, quadNode.X-node.Width, quadNode.Y); err != nil { return nil, err } else { - left = math.Abs(c.W - l) + left = math.Abs(quadNode.W - l) } - if r, err := es.queryCPPN(a, b, c.X+node.Width, c.Y); err != nil { + if r, err := es.queryCPPN(a, b, quadNode.X+node.Width, quadNode.Y); err != nil { return nil, err } else { - right = math.Abs(c.W - r) + right = math.Abs(quadNode.W - r) } - if t, err := es.queryCPPN(a, b, c.X, c.Y-node.Width); err != nil { + if t, err := es.queryCPPN(a, b, quadNode.X, quadNode.Y-node.Width); err != nil { return nil, err } else { - top = math.Abs(c.W - t) + top = math.Abs(quadNode.W - t) } - if b, err := es.queryCPPN(a, b, c.X, c.Y+node.Width); err != nil { + if b, err := es.queryCPPN(a, b, quadNode.X, quadNode.Y+node.Width); err != nil { return nil, err } else { - bottom = math.Abs(c.W - b) + bottom = math.Abs(quadNode.W - b) } } else { - if l, err := es.queryCPPN(c.X-node.Width, c.Y, a, b); err != nil { + if l, err := es.queryCPPN(quadNode.X-node.Width, quadNode.Y, a, b); err != nil { return nil, err } else { - left = math.Abs(c.W - l) + left = math.Abs(quadNode.W - l) } - if r, err := es.queryCPPN(c.X+node.Width, c.Y, a, b); err != nil { + if r, err := es.queryCPPN(quadNode.X+node.Width, quadNode.Y, a, b); err != nil { return nil, err } else { - right = math.Abs(c.W - r) + right = math.Abs(quadNode.W - r) } - if t, err := es.queryCPPN(c.X, c.Y-node.Width, a, b); err != nil { + if t, err := es.queryCPPN(quadNode.X, quadNode.Y-node.Width, a, b); err != nil { return nil, err } else { - top = math.Abs(c.W - t) + top = math.Abs(quadNode.W - t) } - if b, err := es.queryCPPN(c.X, c.Y+node.Width, a, b); err != nil { + if b, err := es.queryCPPN(quadNode.X, quadNode.Y+node.Width, a, b); err != nil { return nil, err } else { - bottom = math.Abs(c.W - b) + bottom = math.Abs(quadNode.W - b) } } - if math.Max(math.Min(top, bottom), math.Min(left, right)) > context.BandingThreshold { + if math.Max(math.Min(top, bottom), math.Min(left, right)) > options.BandingThreshold { // Create new connection specified by QuadPoint(x1,y1,x2,y2,weight) in 4D hypercube var conn *QuadPoint if outgoing { - conn = NewQuadPoint(a, b, c.X, c.Y, c.W) + conn = NewQuadPoint(a, b, quadNode.X, quadNode.Y, quadNode.W) } else { - conn = NewQuadPoint(c.X, c.Y, a, b, c.W) + conn = NewQuadPoint(quadNode.X, quadNode.Y, a, b, quadNode.W) } connections = append(connections, conn) diff --git a/cppn/substrate.go b/cppn/substrate.go index 473a794..5991d26 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -34,8 +34,7 @@ func NewSubstrate(layout SubstrateLayout, nodesActivation neatmath.NodeActivatio // If the useLeo is True thar Link Expression Output extension to the HyperNEAT will be used instead of standard weight threshold // technique of HyperNEAT to determine whether to express link between two nodes or not. With LEO the link expressed based // on value of additional output of the CPPN (if > 0 then expressed) -func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphBuilder SubstrateGraphBuilder, - context *hyperneat.Options) (network.Solver, error) { +func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphBuilder SubstrateGraphBuilder, options *hyperneat.Options) (network.Solver, error) { // check conditions if s.Layout.BiasCount() > 1 { return nil, errors.New("SUBSTRATE: maximum one BIAS node per network supported") @@ -102,10 +101,10 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB return nil, err } else if useLeo && outs[1] > 0 { // add links only when CPPN LEO output signals to - link = createLink(outs[0], bi, hi, context.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { + link = createLink(outs[0], bi, hi, options.WeightRange) + } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { // add only connections with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], bi, hi, context.LinkThreshold, context.WeightRange) + link = createThresholdNormalizedLink(outs[0], bi, hi, options.LinkThreshold, options.WeightRange) } if link != nil { biasList[hi] = link.Weight @@ -136,10 +135,10 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB return nil, err } else if useLeo && outs[1] > 0 { // add links only when CPPN LEO output signals to - link = createLink(outs[0], bi, oi, context.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { + link = createLink(outs[0], bi, oi, options.WeightRange) + } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { // add only connections with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], bi, oi, context.LinkThreshold, context.WeightRange) + link = createThresholdNormalizedLink(outs[0], bi, oi, options.LinkThreshold, options.WeightRange) } if link != nil { biasList[oi] = link.Weight @@ -180,10 +179,10 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB return nil, err } else if useLeo && outs[1] > 0 { // add links only when CPPN LEO output signals to - link = createLink(outs[0], in, hi, context.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { + link = createLink(outs[0], in, hi, options.WeightRange) + } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { // add only connections with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], in, hi, context.LinkThreshold, context.WeightRange) + link = createThresholdNormalizedLink(outs[0], in, hi, options.LinkThreshold, options.WeightRange) } if link != nil { @@ -218,10 +217,10 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB return nil, err } else if useLeo && outs[1] > 0 { // add links only when CPPN LEO output signals to - link = createLink(outs[0], hi, oi, context.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { + link = createLink(outs[0], hi, oi, options.WeightRange) + } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { // add only connections with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], hi, oi, context.LinkThreshold, context.WeightRange) + link = createThresholdNormalizedLink(outs[0], hi, oi, options.LinkThreshold, options.WeightRange) } if link != nil { connections = append(connections, link) @@ -261,10 +260,10 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB return nil, err } else if useLeo && outs[1] > 0 { // add links only when CPPN LEO output signals to - link = createLink(outs[0], in, oi, context.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > context.LinkThreshold { + link = createLink(outs[0], in, oi, options.WeightRange) + } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { // add only connections with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], in, oi, context.LinkThreshold, context.WeightRange) + link = createThresholdNormalizedLink(outs[0], in, oi, options.LinkThreshold, options.WeightRange) } if link != nil { connections = append(connections, link) From cbebfbe69b31d03adb9f64057b5609303a39716b Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 17:53:14 +0300 Subject: [PATCH 29/57] Implemented check for valid network solver created --- cppn/substrate.go | 27 +++++++++++++++++---------- cppn/substrate_test.go | 6 +++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/cppn/substrate.go b/cppn/substrate.go index 5991d26..cab5769 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -2,6 +2,7 @@ package cppn import ( "errors" + "fmt" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" neatmath "github.com/yaricom/goNEAT/v3/neat/math" "github.com/yaricom/goNEAT/v3/neat/network" @@ -49,7 +50,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB totalNeuronCount := lastHidden - connections := make([]*network.FastNetworkLink, 0) + links := make([]*network.FastNetworkLink, 0) biasList := make([]float64, totalNeuronCount) // inline function to find activation type for a given neuron @@ -103,7 +104,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB // add links only when CPPN LEO output signals to link = createLink(outs[0], bi, hi, options.WeightRange) } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only connections with signal exceeding provided threshold + // add only links with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], bi, hi, options.LinkThreshold, options.WeightRange) } if link != nil { @@ -137,7 +138,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB // add links only when CPPN LEO output signals to link = createLink(outs[0], bi, oi, options.WeightRange) } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only connections with signal exceeding provided threshold + // add only links with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], bi, oi, options.LinkThreshold, options.WeightRange) } if link != nil { @@ -181,12 +182,12 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB // add links only when CPPN LEO output signals to link = createLink(outs[0], in, hi, options.WeightRange) } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only connections with signal exceeding provided threshold + // add only links with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], in, hi, options.LinkThreshold, options.WeightRange) } if link != nil { - connections = append(connections, link) + links = append(links, link) // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, in, hi, link.Weight); err != nil { return nil, err @@ -219,11 +220,11 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB // add links only when CPPN LEO output signals to link = createLink(outs[0], hi, oi, options.WeightRange) } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only connections with signal exceeding provided threshold + // add only links with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], hi, oi, options.LinkThreshold, options.WeightRange) } if link != nil { - connections = append(connections, link) + links = append(links, link) // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, hi, oi, link.Weight); err != nil { return nil, err @@ -262,11 +263,11 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB // add links only when CPPN LEO output signals to link = createLink(outs[0], in, oi, options.WeightRange) } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only connections with signal exceeding provided threshold + // add only links with signal exceeding provided threshold link = createThresholdNormalizedLink(outs[0], in, oi, options.LinkThreshold, options.WeightRange) } if link != nil { - connections = append(connections, link) + links = append(links, link) // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, in, oi, link.Weight); err != nil { return nil, err @@ -282,9 +283,15 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB activations[i] = activationForNeuron(i) } + if totalNeuronCount == 0 || len(links) == 0 || len(activations) != totalNeuronCount { + message := fmt.Sprintf("failed to create network solver: links [%d], nodes [%d], activations [%d]", + len(links), totalNeuronCount, len(activations)) + return nil, errors.New(message) + } + // create fast network solver solver := network.NewFastModularNetworkSolver( s.Layout.BiasCount(), s.Layout.InputCount(), s.Layout.OutputCount(), totalNeuronCount, - activations, connections, biasList, nil) + activations, links, biasList, nil) return solver, nil } diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 339e914..4a6423e 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -30,7 +30,7 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) // create solver from substrate - cppn, err := ReadCPPFromGenomeFile(cppnHyperNEATTestGenomePath) + cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") context, err := loadHyperNeatContext(hyperNeatTestConfigFile) @@ -62,7 +62,7 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) // create solver from substrate - cppn, err := ReadCPPFromGenomeFile(cppnLeoHyperNEATTestGenomePath) + cppn, err := FastSolverFromGenomeFile(cppnLeoHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") context, err := loadHyperNeatContext(hyperNeatTestConfigFile) @@ -96,7 +96,7 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) // create solver from substrate - cppn, err := ReadCPPFromGenomeFile(cppnHyperNEATTestGenomePath) + cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") context, err := loadHyperNeatContext(hyperNeatTestConfigFile) From 146f9dac98dc69bbd867e2b7a4e43901e00b5da4 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 19:41:38 +0300 Subject: [PATCH 30/57] Implemented support for LEO in the EvolvableSubstrate.CreateNetworkSolver() --- cppn/common_test.go | 31 +++++++ cppn/cppn.go | 13 ++- cppn/{cpnn_test.go => cppn_test.go} | 28 +++++-- cppn/evolvable_substrate.go | 123 ++++++++++++++++++---------- cppn/evolvable_substrate_test.go | 58 ++++++++----- cppn/quad_tree.go | 46 ++++++++--- cppn/substrate.go | 75 ++++++----------- cppn/substrate_test.go | 28 ++----- executor.go | 4 +- hyperneat/hyper_neat.go | 2 + 10 files changed, 241 insertions(+), 167 deletions(-) create mode 100644 cppn/common_test.go rename cppn/{cpnn_test.go => cppn_test.go} (63%) diff --git a/cppn/common_test.go b/cppn/common_test.go new file mode 100644 index 0000000..9a3bcf3 --- /dev/null +++ b/cppn/common_test.go @@ -0,0 +1,31 @@ +package cppn + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/yaricom/goNEAT/v3/neat/network" + "testing" +) + +func checkNetworkSolverOutputs(solver network.Solver, outExpected []float64, delta float64, t *testing.T) { + signals := []float64{0.9, 5.2, 1.2, 0.6} + err := solver.LoadSensors(signals) + require.NoError(t, err, "failed to load sensors") + + res, err := solver.RecursiveSteps() + require.NoError(t, err, "failed to perform recursive activation") + require.True(t, res, "failed to relax network") + + outs := solver.ReadOutputs() + for i, out := range outs { + assert.InDelta(t, outExpected[i], out, delta, "wrong output at: %d", i) + } +} + +func printGraph(graph SubstrateGraphBuilder, t *testing.T) { + var buf bytes.Buffer + err := graph.Marshal(&buf) + assert.NoError(t, err) + t.Log(buf.String()) +} diff --git a/cppn/cppn.go b/cppn/cppn.go index 16d1f33..9bc822e 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -7,16 +7,13 @@ import ( "github.com/yaricom/goNEAT/v3/neat/genetics" "github.com/yaricom/goNEAT/v3/neat/network" "math" - "os" ) -// ReadCPPFromGenomeFile Reads CPPN from specified genome and creates network solver -func ReadCPPFromGenomeFile(genomePath string) (network.Solver, error) { - if genomeFile, err := os.Open(genomePath); err != nil { +// FastSolverFromGenomeFile Reads CPPN from specified genome and creates network solver +func FastSolverFromGenomeFile(genomePath string) (network.Solver, error) { + if reader, err := genetics.NewGenomeReaderFromFile(genomePath); err != nil { return nil, err - } else if r, err := genetics.NewGenomeReader(genomeFile, genetics.YAMLGenomeEncoding); err != nil { - return nil, err - } else if genome, err := r.Read(); err != nil { + } else if genome, err := reader.Read(); err != nil { return nil, err } else if net, err := genome.Genesis(genome.Id); err != nil { return nil, err @@ -110,6 +107,6 @@ func nodeCPPNValues(n *QuadNode) []float64 { } return accumulator } else { - return []float64{n.W} + return []float64{n.Weight()} } } diff --git a/cppn/cpnn_test.go b/cppn/cppn_test.go similarity index 63% rename from cppn/cpnn_test.go rename to cppn/cppn_test.go index 74c2c05..da3a144 100644 --- a/cppn/cpnn_test.go +++ b/cppn/cppn_test.go @@ -30,12 +30,12 @@ func TestQuadNode_nodeCPPNValues(t *testing.T) { assert.ElementsMatch(t, expected, vals) } -func TestReadCPPFromGenomeFile(t *testing.T) { - cppn, err := ReadCPPFromGenomeFile(cppnHyperNEATTestGenomePath) +func TestFastSolverFromGenomeFile(t *testing.T) { + cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read genome file") require.NotNil(t, cppn, "CPPN expected") - require.Equal(t, cppn.NodeCount(), 7, "wrong nodes number") - require.Equal(t, cppn.LinkCount(), 7, "wrong links number") + require.Equal(t, 7, cppn.NodeCount(), "wrong nodes number") + require.Equal(t, 7, cppn.LinkCount(), "wrong links number") // test query coords := []float64{0.0, 0.0, 0.5, 0.5} @@ -46,6 +46,24 @@ func TestReadCPPFromGenomeFile(t *testing.T) { assert.InDelta(t, 0.4864161653290716, outs[0], 1e-16, "wrong output value") } +func TestFastSolverFromGenomeFile_LEO(t *testing.T) { + cppn, err := FastSolverFromGenomeFile(cppnLeoHyperNEATTestGenomePath) + require.NoError(t, err, "failed to read genome file") + require.NotNil(t, cppn, "CPPN expected") + require.Equal(t, 9, cppn.NodeCount(), "wrong nodes number") + require.Equal(t, 11, cppn.LinkCount(), "wrong links number") + + // test query + coords := []float64{0.0, 0.0, 0.5, 0.5} + outs, err := queryCPPN(coords, cppn) + require.NoError(t, err, "failed to query CPPN") + require.NotNil(t, outs, "output expected") + require.Len(t, outs, 2) + t.Log(outs) + assert.InDelta(t, 0.4117980729033359, outs[0], 1e-16, "wrong output value") + assert.Equal(t, 1.0, outs[1], "wrong LEO value") +} + func buildTree() *QuadNode { root := NewQuadNode(0, 0, 1, 1) root.Nodes = []*QuadNode{ @@ -68,6 +86,6 @@ func buildTree() *QuadNode { func fillW(nodes []*QuadNode, factor float64) { for i, n := range nodes { - n.W = float64(i) * factor + n.CppnOut = []float64{float64(i) * factor} } } diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index fcdab07..199e378 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -2,6 +2,7 @@ package cppn import ( "container/list" + "errors" "fmt" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" neatmath "github.com/yaricom/goNEAT/v3/neat/math" @@ -35,10 +36,22 @@ func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation neat } } +// NewEvolvableSubstrateWithBias creates new instance of evolvable substrate with defined cppnBias value. +// The cppnBias will be provided as first value of the CPPN inputs array. +func NewEvolvableSubstrateWithBias(layout EvolvableSubstrateLayout, nodesActivation neatmath.NodeActivationType, cppnBias float64) *EvolvableSubstrate { + coords := make([]float64, 5) + coords[0] = cppnBias + return &EvolvableSubstrate{ + coords: coords, + Layout: layout, + NodesActivation: nodesActivation, + } +} + // CreateNetworkSolver Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which // used to define connections between network nodes. Optional graph_builder can be provided to collect graph nodes and edges // of created network solver. With graph builder it is possible to save/load network configuration as well as visualize it. -func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuilder SubstrateGraphBuilder, context *eshyperneat.Options) (network.Solver, error) { +func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphBuilder SubstrateGraphBuilder, options *eshyperneat.Options) (network.Solver, error) { es.cppn = cppn // the network layers will be collected in order: bias, input, output, hidden @@ -46,24 +59,34 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil firstOutput := firstInput + es.Layout.InputCount() firstHidden := firstOutput + es.Layout.OutputCount() - connections := make([]*network.FastNetworkLink, 0) - // The map to hold already created connections + links := make([]*network.FastNetworkLink, 0) + // The map to hold already created links connMap := make(map[string]*network.FastNetworkLink) - // The function to add new connection if appropriate - addConnection := func(weight float64, source, target int) (*network.FastNetworkLink, bool) { + // The function to add new link to the network if appropriate + addLink := func(qp *QuadPoint, source, target int) (*network.FastNetworkLink, bool) { key := fmt.Sprintf("%d_%d", source, target) if _, ok := connMap[key]; ok { - // connection already excists + // connection already exists + return nil, false + } + var link *network.FastNetworkLink + if useLeo && qp.CppnOut[1] > 0 { + link = createLink(qp.Weight(), source, target, options.WeightRange) + } else if !useLeo && math.Abs(qp.Weight()) > options.LinkThreshold { + // add only connections with signal exceeding provided threshold + link = createThresholdNormalizedLink(qp.Weight(), source, target, options.LinkThreshold, options.WeightRange) + } + if link != nil { + links = append(links, link) + connMap[key] = link + return link, true + } else { return nil, false } - link := createLink(weight, source, target, context.WeightRange) - connections = append(connections, link) - connMap[key] = link - return link, true } - // Build connections from input nodes to the hidden nodes + // Build links from input nodes to the hidden nodes var root *QuadNode for in := firstInput; in < firstOutput; in++ { // Analyse outgoing connectivity pattern from this input @@ -76,14 +99,14 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } - if root, err = es.quadTreeDivideAndInit(input.X, input.Y, true, context); err != nil { + if root, err = es.quadTreeDivideAndInit(input.X, input.Y, true, options); err != nil { return nil, err } qPoints := make([]*QuadPoint, 0) - if qPoints, err = es.pruneAndExpress(input.X, input.Y, qPoints, root, true, context); err != nil { + if qPoints, err = es.pruneAndExpress(input.X, input.Y, qPoints, root, true, options); err != nil { return nil, err } - // iterate over quad points and add nodes/connections + // iterate over quad points and add nodes/links for _, qp := range qPoints { // add hidden node to the substrate layout if needed targetIndex, err := es.addHiddenNode(qp, firstHidden, graphBuilder) @@ -91,7 +114,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } // add connection - if link, ok := addConnection(qp.Value, in, targetIndex); ok { + if link, ok := addLink(qp, in, targetIndex); ok { // add an edge to the graph if _, err = addEdgeToBuilder(graphBuilder, in, targetIndex, link.Weight); err != nil { return nil, err @@ -103,21 +126,21 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil // Build more hidden nodes into unexplored area through a number of iterations firstHiddenIter := firstHidden lastHidden := firstHiddenIter + es.Layout.HiddenCount() - for step := 0; step < context.ESIterations; step++ { + for step := 0; step < options.ESIterations; step++ { for hi := firstHiddenIter; hi < lastHidden; hi++ { // Analyse outgoing connectivity pattern from this hidden node hidden, err := es.Layout.NodePosition(hi-firstHidden, network.HiddenNeuron) if err != nil { return nil, err } - if root, err = es.quadTreeDivideAndInit(hidden.X, hidden.Y, true, context); err != nil { + if root, err = es.quadTreeDivideAndInit(hidden.X, hidden.Y, true, options); err != nil { return nil, err } qPoints := make([]*QuadPoint, 0) - if qPoints, err = es.pruneAndExpress(hidden.X, hidden.Y, qPoints, root, true, context); err != nil { + if qPoints, err = es.pruneAndExpress(hidden.X, hidden.Y, qPoints, root, true, options); err != nil { return nil, err } - // iterate over quad points and add nodes/connections + // iterate over quad points and add nodes/links for _, qp := range qPoints { // add hidden node to the substrate layout if needed targetIndex, err := es.addHiddenNode(qp, firstHidden, graphBuilder) @@ -125,7 +148,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } // add connection - if link, ok := addConnection(qp.Value, hi, targetIndex); ok { + if link, ok := addLink(qp, hi, targetIndex); ok { // add an edge to the graph if _, err = addEdgeToBuilder(graphBuilder, hi, targetIndex, link.Weight); err != nil { return nil, err @@ -151,15 +174,15 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } - if root, err = es.quadTreeDivideAndInit(output.X, output.Y, false, context); err != nil { + if root, err = es.quadTreeDivideAndInit(output.X, output.Y, false, options); err != nil { return nil, err } qPoints := make([]*QuadPoint, 0) - if qPoints, err = es.pruneAndExpress(output.X, output.Y, qPoints, root, false, context); err != nil { + if qPoints, err = es.pruneAndExpress(output.X, output.Y, qPoints, root, false, options); err != nil { return nil, err } - // iterate over quad points and add nodes/connections where appropriate + // iterate over quad points and add nodes/links where appropriate for _, qp := range qPoints { nodePoint := NewPointF(qp.X1, qp.Y1) sourceIndex := es.Layout.IndexOfHidden(nodePoint) @@ -168,7 +191,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil sourceIndex += firstHidden // adjust index to the global indexes space // add connection - if link, ok := addConnection(qp.Value, sourceIndex, oi); ok { + if link, ok := addLink(qp, sourceIndex, oi); ok { // add an edge to the graph if _, err = addEdgeToBuilder(graphBuilder, sourceIndex, oi, link.Weight); err != nil { return nil, err @@ -194,9 +217,14 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil } // create fast network solver + if totalNeuronCount == 0 || len(links) == 0 || len(activations) != totalNeuronCount { + message := fmt.Sprintf("failed to create network solver: links [%d], nodes [%d], activations [%d]", + len(links), totalNeuronCount, len(activations)) + return nil, errors.New(message) + } solver := network.NewFastModularNetworkSolver( 0, es.Layout.InputCount(), es.Layout.OutputCount(), totalNeuronCount, - activations, connections, nil, nil) // No BIAS + activations, links, nil, nil) // No BIAS return solver, nil } @@ -242,15 +270,15 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, NewQuadNode(p.X+p.Width/2.0, p.Y+p.Width/2.0, p.Width/2.0, p.Level+1), } - for _, c := range p.Nodes { + for _, node := range p.Nodes { if outgoing { // Querying connection from input or hidden node (Outgoing connectivity pattern) - if c.W, err = es.queryCPPN(a, b, c.X, c.Y); err != nil { + if node.CppnOut, err = es.queryCPPN(a, b, node.X, node.Y); err != nil { return nil, err } } else { // Querying connection to output node (Incoming connectivity pattern) - if c.W, err = es.queryCPPN(c.X, c.Y, a, b); err != nil { + if node.CppnOut, err = es.queryCPPN(node.X, node.Y, a, b); err != nil { return nil, err } } @@ -296,43 +324,43 @@ func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadP if l, err := es.queryCPPN(a, b, quadNode.X-node.Width, quadNode.Y); err != nil { return nil, err } else { - left = math.Abs(quadNode.W - l) + left = math.Abs(quadNode.Weight() - l[0]) } if r, err := es.queryCPPN(a, b, quadNode.X+node.Width, quadNode.Y); err != nil { return nil, err } else { - right = math.Abs(quadNode.W - r) + right = math.Abs(quadNode.Weight() - r[0]) } if t, err := es.queryCPPN(a, b, quadNode.X, quadNode.Y-node.Width); err != nil { return nil, err } else { - top = math.Abs(quadNode.W - t) + top = math.Abs(quadNode.Weight() - t[0]) } if b, err := es.queryCPPN(a, b, quadNode.X, quadNode.Y+node.Width); err != nil { return nil, err } else { - bottom = math.Abs(quadNode.W - b) + bottom = math.Abs(quadNode.Weight() - b[0]) } } else { if l, err := es.queryCPPN(quadNode.X-node.Width, quadNode.Y, a, b); err != nil { return nil, err } else { - left = math.Abs(quadNode.W - l) + left = math.Abs(quadNode.Weight() - l[0]) } if r, err := es.queryCPPN(quadNode.X+node.Width, quadNode.Y, a, b); err != nil { return nil, err } else { - right = math.Abs(quadNode.W - r) + right = math.Abs(quadNode.Weight() - r[0]) } if t, err := es.queryCPPN(quadNode.X, quadNode.Y-node.Width, a, b); err != nil { return nil, err } else { - top = math.Abs(quadNode.W - t) + top = math.Abs(quadNode.Weight() - t[0]) } if b, err := es.queryCPPN(quadNode.X, quadNode.Y+node.Width, a, b); err != nil { return nil, err } else { - bottom = math.Abs(quadNode.W - b) + bottom = math.Abs(quadNode.Weight() - b[0]) } } @@ -340,9 +368,9 @@ func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadP // Create new connection specified by QuadPoint(x1,y1,x2,y2,weight) in 4D hypercube var conn *QuadPoint if outgoing { - conn = NewQuadPoint(a, b, quadNode.X, quadNode.Y, quadNode.W) + conn = NewQuadPoint(a, b, quadNode.X, quadNode.Y, quadNode) } else { - conn = NewQuadPoint(quadNode.X, quadNode.Y, a, b, quadNode.W) + conn = NewQuadPoint(quadNode.X, quadNode.Y, a, b, quadNode) } connections = append(connections, conn) @@ -355,15 +383,20 @@ func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadP // Query CPPN associated with this substrate for specified Hypercube coordinate and returns value produced or error if // operation failed -func (es *EvolvableSubstrate) queryCPPN(x1, y1, x2, y2 float64) (float64, error) { - es.coords[0] = x1 - es.coords[1] = y1 - es.coords[2] = x2 - es.coords[3] = y2 +func (es *EvolvableSubstrate) queryCPPN(x1, y1, x2, y2 float64) ([]float64, error) { + offset := 0 + if len(es.coords) == 5 { + // CPPN bias defined + offset = 1 + } + es.coords[offset] = x1 + es.coords[offset+1] = y1 + es.coords[offset+2] = x2 + es.coords[offset+3] = y2 if outs, err := queryCPPN(es.coords, es.cppn); err != nil { - return math.MaxFloat64, err + return nil, err } else { - return outs[0], nil + return outs, nil } } diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index dd87dbf..fde34fc 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -17,43 +17,57 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation) - cppn, err := ReadCPPFromGenomeFile(cppnHyperNEATTestGenomePath) + cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") - context, err := loadESHyperNeatContext(esHyperNeatTestConfigFile) + context, err := loadESHyperNeatOptions(esHyperNeatTestConfigFile) require.NoError(t, err, "failed to read ESHyperNEAT context") // test solver creation - graph := NewSubstrateGraphMLBuilder("", false) - solver, err := substr.CreateNetworkSolver(cppn, graph, context) + graph := NewSubstrateGraphMLBuilder("TestEvolvableSubstrate_CreateNetworkSolver", false) + solver, err := substr.CreateNetworkSolver(cppn, false, graph, context) require.NoError(t, err, "failed to create solver") - //var buf bytes.Buffer - //err = graph.Marshal(&buf) - //t.Log(buf.String()) + printGraph(graph, t) totalNodeCount := inputCount + outputCount + layout.HiddenCount() assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong total node count") - assert.Equal(t, 10, solver.LinkCount(), "wrong link number") + assert.Equal(t, 8, solver.LinkCount(), "wrong link number") - // test outputs - signals := []float64{0.9, 5.2, 1.2, 0.6} - err = solver.LoadSensors(signals) - assert.NoError(t, err, "failed to load sensors") + // check outputs + outExpected := []float64{0.5, 0.5} + checkNetworkSolverOutputs(solver, outExpected, 1e-8, t) +} + +func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { + inputCount, outputCount := 4, 2 + layout, err := NewMappedEvolvableSubstrateLayout(inputCount, outputCount) + require.NoError(t, err, "failed to create layout") + + substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation) + + cppn, err := FastSolverFromGenomeFile(cppnLeoHyperNEATTestGenomePath) + require.NoError(t, err, "failed to read CPPN") + context, err := loadESHyperNeatOptions(esHyperNeatTestConfigFile) + require.NoError(t, err, "failed to read ESHyperNEAT context") + + // test solver creation + graph := NewSubstrateGraphMLBuilder("TestEvolvableSubstrate_CreateNetworkSolver", false) + solver, err := substr.CreateNetworkSolver(cppn, true, graph, context) + require.NoError(t, err, "failed to create solver") - res, err := solver.RecursiveSteps() - require.NoError(t, err, "failed to propagate recursive activation") - require.True(t, res, "failed to relax network") + printGraph(graph, t) - outs := solver.ReadOutputs() + totalNodeCount := inputCount + outputCount + layout.HiddenCount() + assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong total node count") + assert.Equal(t, 8, solver.LinkCount(), "wrong link number") + + // check outputs outExpected := []float64{0.5, 0.5} - delta := 0.0000000001 - for i, out := range outs { - assert.InDelta(t, outExpected[i], out, delta, "unexpected output: %v at: %d", out, i) - } + checkNetworkSolverOutputs(solver, outExpected, 1e-8, t) } -// Loads ES-HyperNeat context from provided config file's path -func loadESHyperNeatContext(configPath string) (*eshyperneat.Options, error) { +// Loads ES-HyperNeat options from provided config file's path +func loadESHyperNeatOptions(configPath string) (*eshyperneat.Options, error) { if ctx, err := eshyperneat.LoadYAMLConfigFile(configPath); err != nil { return nil, err } else { diff --git a/cppn/quad_tree.go b/cppn/quad_tree.go index db3c72a..9141a94 100644 --- a/cppn/quad_tree.go +++ b/cppn/quad_tree.go @@ -19,17 +19,33 @@ func (p *PointF) String() string { type QuadPoint struct { // The associated coordinates X1, X2, Y1, Y2 float64 - // The value for this point - Value float64 + // The CPPN outputs for this point + CppnOut []float64 +} + +func (q *QuadPoint) Weight() float64 { + return q.CppnOut[0] } func (q *QuadPoint) String() string { - return fmt.Sprintf("((%f, %f),(%f, %f)) = %f", q.X1, q.Y1, q.X2, q.Y2, q.Value) + str := fmt.Sprintf("((%f, %f),(%f, %f)) = %f", q.X1, q.Y1, q.X2, q.Y2, q.Weight()) + if len(q.CppnOut) > 1 { + var status string + if q.CppnOut[1] > 0 { + status = "enabled" + } else { + status = "disabled" + } + str = fmt.Sprintf("%s [%s]", str, status) + } + return str } // NewQuadPoint Creates new quad point -func NewQuadPoint(x1, y1, x2, y2, value float64) *QuadPoint { - return &QuadPoint{X1: x1, Y1: y1, X2: x2, Y2: y2, Value: value} +func NewQuadPoint(x1, y1, x2, y2 float64, node *QuadNode) *QuadPoint { + outs := make([]float64, len(node.CppnOut)) + copy(outs, node.CppnOut) + return &QuadPoint{X1: x1, Y1: y1, X2: x2, Y2: y2, CppnOut: outs} } // QuadNode Defines quad-tree node to model 4 dimensional hypercube @@ -39,8 +55,8 @@ type QuadNode struct { // The width of this quad-tree node's square Width float64 - // The CPPN activation level for this node - W float64 + // The CPPN outputs for this node + CppnOut []float64 // The level of this node in the quad-tree Level int @@ -48,18 +64,22 @@ type QuadNode struct { Nodes []*QuadNode } +func (q *QuadNode) Weight() float64 { + return q.CppnOut[0] +} + func (q *QuadNode) String() string { - return fmt.Sprintf("((%f, %f), %f) = %f at %d", q.X, q.Y, q.Width, q.W, q.Level) + return fmt.Sprintf("((%f, %f), %f) = %f at %d", q.X, q.Y, q.Width, q.CppnOut, q.Level) } // NewQuadNode Creates new quad-node with given parameters func NewQuadNode(x, y, width float64, level int) *QuadNode { node := QuadNode{ - X: x, - Y: y, - Width: width, - W: 0.0, - Level: level, + X: x, + Y: y, + Width: width, + CppnOut: []float64{0.0}, + Level: level, } return &node } diff --git a/cppn/substrate.go b/cppn/substrate.go index cab5769..b3217f7 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -65,7 +65,6 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } // give bias inputs to all hidden and output nodes. - var link *network.FastNetworkLink coordinates := make([]float64, 4) for bi := firstBias; bi < firstInput; bi++ { @@ -97,17 +96,9 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } } // find connection weight - link = nil - if outs, err := queryCPPN(coordinates, cppn); err != nil { + if link, err := fastNetworkLink(coordinates, cppn, useLeo, bi, hi, options); err != nil { return nil, err - } else if useLeo && outs[1] > 0 { - // add links only when CPPN LEO output signals to - link = createLink(outs[0], bi, hi, options.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only links with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], bi, hi, options.LinkThreshold, options.WeightRange) - } - if link != nil { + } else if link != nil { biasList[hi] = link.Weight // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, bi, hi, link.Weight); err != nil { @@ -131,17 +122,9 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } } // find connection weight - link = nil - if outs, err := queryCPPN(coordinates, cppn); err != nil { + if link, err := fastNetworkLink(coordinates, cppn, useLeo, bi, oi, options); err != nil { return nil, err - } else if useLeo && outs[1] > 0 { - // add links only when CPPN LEO output signals to - link = createLink(outs[0], bi, oi, options.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only links with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], bi, oi, options.LinkThreshold, options.WeightRange) - } - if link != nil { + } else if link != nil { biasList[oi] = link.Weight // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, bi, oi, link.Weight); err != nil { @@ -175,18 +158,9 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB coordinates[3] = hiddenPosition.Y } // find connection weight - link = nil - if outs, err := queryCPPN(coordinates, cppn); err != nil { + if link, err := fastNetworkLink(coordinates, cppn, useLeo, in, hi, options); err != nil { return nil, err - } else if useLeo && outs[1] > 0 { - // add links only when CPPN LEO output signals to - link = createLink(outs[0], in, hi, options.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only links with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], in, hi, options.LinkThreshold, options.WeightRange) - - } - if link != nil { + } else if link != nil { links = append(links, link) // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, in, hi, link.Weight); err != nil { @@ -213,17 +187,9 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB coordinates[3] = outputPosition.Y } // find connection weight - link = nil - if outs, err := queryCPPN(coordinates, cppn); err != nil { + if link, err := fastNetworkLink(coordinates, cppn, useLeo, hi, oi, options); err != nil { return nil, err - } else if useLeo && outs[1] > 0 { - // add links only when CPPN LEO output signals to - link = createLink(outs[0], hi, oi, options.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only links with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], hi, oi, options.LinkThreshold, options.WeightRange) - } - if link != nil { + } else if link != nil { links = append(links, link) // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, hi, oi, link.Weight); err != nil { @@ -256,17 +222,9 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB coordinates[3] = outputPosition.Y } // find connection weight - link = nil - if outs, err := queryCPPN(coordinates, cppn); err != nil { + if link, err := fastNetworkLink(coordinates, cppn, useLeo, in, oi, options); err != nil { return nil, err - } else if useLeo && outs[1] > 0 { - // add links only when CPPN LEO output signals to - link = createLink(outs[0], in, oi, options.WeightRange) - } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { - // add only links with signal exceeding provided threshold - link = createThresholdNormalizedLink(outs[0], in, oi, options.LinkThreshold, options.WeightRange) - } - if link != nil { + } else if link != nil { links = append(links, link) // add node and edge to graph if _, err := addEdgeToBuilder(graphBuilder, in, oi, link.Weight); err != nil { @@ -295,3 +253,16 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB activations, links, biasList, nil) return solver, nil } + +func fastNetworkLink(coordinates []float64, cppn network.Solver, useLeo bool, source, target int, options *hyperneat.Options) (*network.FastNetworkLink, error) { + if outs, err := queryCPPN(coordinates, cppn); err != nil { + return nil, err + } else if useLeo && outs[1] > 0 { + // add links only when CPPN LEO output signals to + return createLink(outs[0], source, target, options.WeightRange), nil + } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { + // add only links with signal exceeding provided threshold + return createThresholdNormalizedLink(outs[0], source, target, options.LinkThreshold, options.WeightRange), nil + } + return nil, nil +} diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 4a6423e..99d9dc4 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" "github.com/yaricom/goNEAT/v3/neat/math" - "github.com/yaricom/goNEAT/v3/neat/network" "testing" ) @@ -50,7 +49,7 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { // test outputs outExpected := []float64{0.6427874813512032, 0.8685335941574246} - checkNetworkSolverOutputs(solver, outExpected, t) + checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { @@ -73,16 +72,18 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { solver, err := substr.CreateNetworkSolver(cppn, true, graph, context) require.NoError(t, err, "failed to create network solver") + printGraph(graph, t) + // test solver totalNodeCount := biasCount + inputCount + hiddenCount + outputCount assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong nodes number") - totalLinkCount := 14 + totalLinkCount := 16 assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - outExpected := []float64{0.5000001657646664, 0.5000003552761682} - checkNetworkSolverOutputs(solver, outExpected, t) + outExpected := []float64{0.5, 0.5} + checkNetworkSolverOutputs(solver, outExpected, 1e-5, t) } func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { @@ -124,22 +125,7 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { // test outputs outExpected := []float64{0.6427874813512032, 0.8685335941574246} - checkNetworkSolverOutputs(solver, outExpected, t) -} - -func checkNetworkSolverOutputs(solver network.Solver, outExpected []float64, t *testing.T) { - signals := []float64{0.9, 5.2, 1.2, 0.6} - err := solver.LoadSensors(signals) - require.NoError(t, err, "failed to load sensors") - - res, err := solver.RecursiveSteps() - require.NoError(t, err, "failed to perform recursive activation") - require.True(t, res, "failed to relax network") - - outs := solver.ReadOutputs() - for i, out := range outs { - assert.Equal(t, outExpected[i], out, "wrong output at: %d", i) - } + checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } // Loads HyperNeat context from provided config file's path diff --git a/executor.go b/executor.go index 957ae40..b67886d 100644 --- a/executor.go +++ b/executor.go @@ -25,6 +25,7 @@ func main() { var experimentName = flag.String("experiment", "retina", "The name of experiment to run. [retina]") var speciesTarget = flag.Int("species_target", 15, "The target number of species to maintain.") var speciesCompatAdjustFreq = flag.Int("species_adjust_freq", 10, "The frequency of species compatibility threshold adjustments when trying to maintain their number.") + var useLeo = flag.Bool("use_leo", false, "The flag to indicate if Link Expression Output should be enabled in CPPN") var logLevel = flag.String("log-level", "", "The logger level to be used. Overrides the one set in configuration.") var trialsCount = flag.Int("trials", 0, "The number of trials for experiment. Overrides the one set in configuration.") @@ -101,7 +102,8 @@ func main() { if env, err := retina.NewRetinaEnvironment(retina.CreateRetinaDataset(), 4); err != nil { log.Fatalf("Failed to create retina environment, reason: %s", err) } else { - generationEvaluator, trialObserver = retina.NewGenerationEvaluator(*outDirPath, env, *speciesTarget, *speciesCompatAdjustFreq) + generationEvaluator, trialObserver = retina.NewGenerationEvaluator( + *outDirPath, env, *speciesTarget, *speciesCompatAdjustFreq, *useLeo) } default: log.Fatalf("Unsupported experiment name requested: %s\n", *experimentName) diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index 93d2a9f..cda8c1c 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -24,6 +24,8 @@ type Options struct { // The substrate activation function SubstrateActivator SubstrateActivatorType `yaml:"substrate_activator"` + // The BIAS value for CPPN network + CppnBias float64 `yaml:"cppn_bias,omitempty"` } // LoadYAMLOptions is to read HyperNEAT options from the provided reader From fc02c46f0d1c1f055c512239c585dc86d8d84d4d Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 19:42:07 +0300 Subject: [PATCH 31/57] Fixed test genome configuration --- data/test/test_cppn_leo_hyperneat_genome.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/data/test/test_cppn_leo_hyperneat_genome.yml b/data/test/test_cppn_leo_hyperneat_genome.yml index bcf208d..c347f26 100644 --- a/data/test/test_cppn_leo_hyperneat_genome.yml +++ b/data/test/test_cppn_leo_hyperneat_genome.yml @@ -15,14 +15,14 @@ genome: nodes: - {id: 1, trait_id: 0, type: BIAS, activation: NullActivation} # The input nodes - sensors - - {id: 2, trait_id: 0, type: INPT, activation: NullActivation} - - {id: 3, trait_id: 0, type: INPT, activation: NullActivation} - - {id: 4, trait_id: 0, type: INPT, activation: NullActivation} - - {id: 5, trait_id: 0, type: INPT, activation: NullActivation} + - {id: 2, trait_id: 0, type: INPT, activation: NullActivation} # x1 + - {id: 3, trait_id: 0, type: INPT, activation: NullActivation} # y1 + - {id: 4, trait_id: 0, type: INPT, activation: NullActivation} # x2 + - {id: 5, trait_id: 0, type: INPT, activation: NullActivation} # y2 # The weight output - - {id: 6, trait_id: 0, type: OUTP, activation: LinearActivation} - # The LEO output - using Gaussian to force local connectivity - - {id: 7, trait_id: 0, type: OUTP, activation: LinearActivation} + - {id: 6, trait_id: 0, type: OUTP, activation: TanhActivation} + # The LEO output + - {id: 7, trait_id: 0, type: OUTP, activation: StepActivation} # The hidden nodes - {id: 8, trait_id: 0, type: HIDN, activation: SineActivation} - {id: 9, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation} @@ -37,7 +37,7 @@ genome: - {src_id: 1, tgt_id: 8, weight: 0.4, trait_id: 6, innov_num: 6, mut_num: 0, recurrent: false, enabled: true} - {src_id: 8, tgt_id: 6, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} # The LEO part with local connectivity among X axis - - {src_id: 2, tgt_id: 9, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} - {src_id: 4, tgt_id: 9, weight: 0.3, trait_id: 4, innov_num: 4, mut_num: 0, recurrent: false, enabled: true} - {src_id: 1, tgt_id: 9, weight: 0.4, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 1, tgt_id: 7, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} - {src_id: 9, tgt_id: 7, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} \ No newline at end of file From 08216823f6e1a20ce53402306a83735aa54ea39c Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 19:43:04 +0300 Subject: [PATCH 32/57] Fixed TestEvolvableSubstrate_CreateNetworkSolver_LEO --- cppn/evolvable_substrate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index fde34fc..57aee8f 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -59,7 +59,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { totalNodeCount := inputCount + outputCount + layout.HiddenCount() assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong total node count") - assert.Equal(t, 8, solver.LinkCount(), "wrong link number") + assert.Equal(t, 19, solver.LinkCount(), "wrong link number") // check outputs outExpected := []float64{0.5, 0.5} From d5e0a4444b8c63244cbfa6d7720cfe86e4447dad Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 19:43:22 +0300 Subject: [PATCH 33/57] Updated goNeat dependency version --- go.mod | 2 +- go.sum | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 99c0017..0e64723 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.1 github.com/yaricom/goGraphML v1.1.0 - github.com/yaricom/goNEAT/v3 v3.0.1 + github.com/yaricom/goNEAT/v3 v3.0.3 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index edde1cd..d62e953 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= @@ -44,8 +42,10 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yaricom/goGraphML v1.1.0 h1:CrM6yGmZ8Azv2Id2KIzei277MPe5YFzKkOOJu45uOBM= github.com/yaricom/goGraphML v1.1.0/go.mod h1:OM0MGAy6tdufwNYPW9BS2mR6NMArD7RtlakyTs+A3Vk= -github.com/yaricom/goNEAT/v3 v3.0.1 h1:C5gYl2ErevPFDl5gcbav3JhFRADKCkiWEa4jUM/y9hA= -github.com/yaricom/goNEAT/v3 v3.0.1/go.mod h1:gkZxHLDW3dRwacNk0ITgt9okkCSnFeTbosWfLFnYucg= +github.com/yaricom/goNEAT/v3 v3.0.2 h1:w70mMJPRTTo98tAL1s42H7yQ0pylMP6fZhxFdxGD9bo= +github.com/yaricom/goNEAT/v3 v3.0.2/go.mod h1:gkZxHLDW3dRwacNk0ITgt9okkCSnFeTbosWfLFnYucg= +github.com/yaricom/goNEAT/v3 v3.0.3 h1:T7gHNw2l/CfCkC4ebettHekW4z9ztBSnWHMwrhG1Y3I= +github.com/yaricom/goNEAT/v3 v3.0.3/go.mod h1:gkZxHLDW3dRwacNk0ITgt9okkCSnFeTbosWfLFnYucg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -54,8 +54,6 @@ golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -80,7 +78,6 @@ golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= @@ -91,9 +88,8 @@ gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 004ba27d2d894dc01918e88f18db86af3e535b55 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 19:52:04 +0300 Subject: [PATCH 34/57] Updated retina experiment runner to support LEO, CPPN bias, provide more detailed intermediate output --- examples/retina/retina.go | 54 +++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 1dc3db8..0df48d6 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -34,17 +34,20 @@ type generationEvaluator struct { numSpeciesTarget int // The species compatibility threshold adjustment frequency compatAdjustFreq int + // The flag to indicate if Link Expression Output should be enabled in CPPN + useLeo bool } // NewGenerationEvaluator is to create new generation's evaluator for retina experiment. The numSpeciesTarget specifies the // target number of species to maintain in the population. If the number of species differ from the numSpeciesTarget it // will be automatically adjusted with compatAdjustFreq frequency, i.e., at each epoch % compatAdjustFreq == 0 -func NewGenerationEvaluator(outDir string, env *Environment, numSpeciesTarget, compatAdjustFreq int) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { +func NewGenerationEvaluator(outDir string, env *Environment, numSpeciesTarget, compatAdjustFreq int, useLeo bool) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { evaluator := &generationEvaluator{ outDir: outDir, env: env, numSpeciesTarget: numSpeciesTarget, compatAdjustFreq: compatAdjustFreq, + useLeo: useLeo, } return evaluator, evaluator } @@ -71,15 +74,24 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population return neat.ErrNEATOptionsNotFound } // Evaluate each organism on a test - maxPopulationFitness := 0.0 + var ( + maxPopulationFitness = 0.0 + bestLinkCount = 0 + bestNodeCount = 0 + ) + var bestSubstrateSolver network.Solver + for _, organism := range population.Organisms { - isWinner, err := e.organismEvaluate(ctx, organism) + isWinner, solver, err := e.organismEvaluate(ctx, organism) if err != nil { return err } if organism.Fitness > maxPopulationFitness { maxPopulationFitness = organism.Fitness + bestLinkCount = organism.Phenotype.LinkCount() + bestNodeCount = organism.Phenotype.NodeCount() + bestSubstrateSolver = solver } if isWinner && (epoch.Champion == nil || organism.Fitness > epoch.Champion.Fitness) { @@ -145,36 +157,40 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population // adjust species count by keeping it constant examples.AdjustSpeciesNumber(speciesCount, epoch.Id, e.compatAdjustFreq, e.numSpeciesTarget, options) - neat.InfoLog(fmt.Sprintf("%d species -> %d organisms [compatibility threshold: %.1f, target: %d], max fitness of population: %.2f\n", - speciesCount, len(population.Organisms), options.CompatThreshold, e.numSpeciesTarget, maxPopulationFitness)) + neat.InfoLog( + fmt.Sprintf("%d species -> %d organisms [compatibility threshold: %.1f, target: %d]\nbest CPNN organism [fitness: %.2f, links: %d, nodes: %d], best solver [links: %d, nodes: %d]", + speciesCount, len(population.Organisms), options.CompatThreshold, e.numSpeciesTarget, + maxPopulationFitness, bestLinkCount, bestNodeCount, + bestSubstrateSolver.LinkCount(), bestSubstrateSolver.NodeCount())) } return nil } // organismEvaluate evaluates an individual phenotype network with retina experiment and returns true if its won -func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *genetics.Organism) (bool, error) { +func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *genetics.Organism) (bool, network.Solver, error) { options, ok := eshyperneat.FromContext(ctx) if !ok { - return false, eshyperneat.ErrESHyperNEATOptionsNotFound + return false, nil, eshyperneat.ErrESHyperNEATOptionsNotFound } // get CPPN network solver - cppnSolver, err := organism.Phenotype.FastNetworkSolver() - if err != nil { - return false, err - } + //cppnSolver, err := organism.Phenotype.FastNetworkSolver() + //if err != nil { + // return false, err + //} + cppnSolver := organism.Phenotype // create substrate layout inputCount := e.env.inputSize * 2 // left + right pixels of visual object layout, err := cppn.NewMappedEvolvableSubstrateLayout(inputCount, 2) if err != nil { - return false, err + return false, nil, err } // create ES-HyperNEAT solver - substr := cppn.NewEvolvableSubstrate(layout, options.SubstrateActivator.SubstrateActivationType) + substr := cppn.NewEvolvableSubstrateWithBias(layout, options.SubstrateActivator.SubstrateActivationType, options.CppnBias) graph := cppn.NewSubstrateGraphMLBuilder("retina ES-HyperNEAT", false) - solver, err := substr.CreateNetworkSolver(cppnSolver, graph, options) + solver, err := substr.CreateNetworkSolver(cppnSolver, e.useLeo, graph, options) if err != nil { - return false, err + return false, nil, err } // Evaluate the detector ANN against 256 combinations of the left and the right visual objects @@ -185,7 +201,7 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen // Evaluate outputted predictions loss, err := evaluateNetwork(solver, leftObj, rightObj) if err != nil { - return false, err + return false, nil, err } errorSum += loss count += 1.0 @@ -194,9 +210,9 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen } // flush solver if flushed, err := solver.Flush(); err != nil { - return false, err + return false, nil, err } else if !flushed { - return false, errors.New("failed to flush solver after evaluation") + return false, nil, errors.New("failed to flush solver after evaluation") } } } @@ -224,7 +240,7 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen solver.NodeCount(), solver.LinkCount(), cppnSolver.NodeCount(), cppnSolver.LinkCount())) } - return isWinner, nil + return isWinner, solver, nil } // evaluateNetwork is to evaluate provided network solver using provided visual objects to test prediction performance. From cf7a9b58eafedae0aab3f80dbc58873ca99f91bd Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 23 Jun 2022 19:54:02 +0300 Subject: [PATCH 35/57] Fixed CPPN genome of retina experiment to implement X-locality and Geometry Seeding --- data/retina/cppn_genome.yml | 46 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/data/retina/cppn_genome.yml b/data/retina/cppn_genome.yml index 13259cf..cd58b02 100644 --- a/data/retina/cppn_genome.yml +++ b/data/retina/cppn_genome.yml @@ -3,33 +3,37 @@ genome: id: 1 # The traits used in this genome traits: - - {id: 1, params: [0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]} - - {id: 2, params: [0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]} - - {id: 3, params: [0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]} - - {id: 4, params: [0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]} - - {id: 5, params: [0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]} - - {id: 6, params: [0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]} - - {id: 7, params: [0.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]} + - { id: 1, params: [ 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } + - { id: 2, params: [ 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } + - { id: 3, params: [ 0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } + - { id: 4, params: [ 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } + - { id: 5, params: [ 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } + - { id: 6, params: [ 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } + - { id: 7, params: [ 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } + - { id: 8, params: [ 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } # The neuron nodes for this genome nodes: - - {id: 1, trait_id: 0, type: BIAS, activation: LinearActivation} + - { id: 1, trait_id: 0, type: BIAS, activation: NullActivation } # The input nodes - sensors - - {id: 2, trait_id: 0, type: INPT, activation: LinearActivation} - - {id: 3, trait_id: 0, type: INPT, activation: LinearActivation} - - {id: 4, trait_id: 0, type: INPT, activation: LinearActivation} - - {id: 5, trait_id: 0, type: INPT, activation: LinearActivation} + - { id: 2, trait_id: 0, type: INPT, activation: NullActivation } # x1 + - { id: 3, trait_id: 0, type: INPT, activation: NullActivation } # y1 + - { id: 4, trait_id: 0, type: INPT, activation: NullActivation } # x2 + - { id: 5, trait_id: 0, type: INPT, activation: NullActivation } # y2 # The output nodes - actuators - - {id: 6, trait_id: 0, type: OUTP, activation: TanhActivation} + - { id: 6, trait_id: 0, type: OUTP, activation: TanhActivation } + - { id: 7, trait_id: 0, type: OUTP, activation: StepActivation } # LEO # The hidden node - - {id: 7, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation} + - { id: 8, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G1 + - { id: 9, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G2 # The genes - connection between neuron nodes within this genome genes: - - {src_id: 1, tgt_id: 6, weight: 0.1, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 2, tgt_id: 6, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 3, tgt_id: 7, weight: 0.2, trait_id: 3, innov_num: 3, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 4, tgt_id: 7, weight: 0.3, trait_id: 4, innov_num: 4, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 5, tgt_id: 6, weight: 0.5, trait_id: 5, innov_num: 5, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 1, tgt_id: 7, weight: 0.4, trait_id: 6, innov_num: 6, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 7, tgt_id: 6, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} \ No newline at end of file + - { src_id: 1, tgt_id: 9, weight: 0.5, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 1, tgt_id: 7, weight: 0.5, trait_id: 6, innov_num: 2, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 2, tgt_id: 8, weight: 0.5, trait_id: 2, innov_num: 6, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 3, tgt_id: 9, weight: 0.5, trait_id: 3, innov_num: 7, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 4, tgt_id: 8, weight: 0.5, trait_id: 4, innov_num: 8, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 5, tgt_id: 9, weight: 0.5, trait_id: 5, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 8, tgt_id: 7, weight: 0.5, trait_id: 7, innov_num: 4, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 9, tgt_id: 6, weight: 0.5, trait_id: 8, innov_num: 5, mut_num: 0, recurrent: false, enabled: true } \ No newline at end of file From 7d9d4ec3734bac68167e576a522b9f28009390e8 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 12 Apr 2024 21:24:34 +0300 Subject: [PATCH 36/57] Fixed to use the latest version `goNEAT` library. --- cppn/common_test.go | 2 +- cppn/cppn.go | 4 +- cppn/evolvable_substrate.go | 10 +-- cppn/evolvable_substrate_layout.go | 2 +- cppn/evolvable_substrate_layout_test.go | 2 +- cppn/evolvable_substrate_test.go | 2 +- cppn/substrate.go | 4 +- cppn/substrate_graph_builder.go | 4 +- cppn/substrate_graph_builder_test.go | 4 +- cppn/substrate_layout.go | 2 +- cppn/substrate_layout_test.go | 2 +- cppn/substrate_test.go | 2 +- data/retina/cppn_genome.yml | 24 ++--- data/retina/es_hyper.neat.yml | 40 ++++----- eshyperneat/es_hyper_neat_test.go | 2 +- examples/common.go | 2 +- examples/retina/retina.go | 44 +++++---- executor.go | 15 ++-- go.mod | 18 ++-- go.sum | 114 +++++++----------------- hyperneat/hyper_neat.go | 2 +- hyperneat/hyper_neat_test.go | 2 +- 22 files changed, 133 insertions(+), 170 deletions(-) diff --git a/cppn/common_test.go b/cppn/common_test.go index 9a3bcf3..51bac62 100644 --- a/cppn/common_test.go +++ b/cppn/common_test.go @@ -4,7 +4,7 @@ import ( "bytes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/network" "testing" ) diff --git a/cppn/cppn.go b/cppn/cppn.go index 9bc822e..d455dcd 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -4,8 +4,8 @@ package cppn import ( "errors" - "github.com/yaricom/goNEAT/v3/neat/genetics" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/genetics" + "github.com/yaricom/goNEAT/v4/neat/network" "math" ) diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index 199e378..332f3e7 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - neatmath "github.com/yaricom/goNEAT/v3/neat/math" - "github.com/yaricom/goNEAT/v3/neat/network" + neatmath "github.com/yaricom/goNEAT/v4/neat/math" + "github.com/yaricom/goNEAT/v4/neat/network" "math" ) @@ -217,9 +217,9 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bo } // create fast network solver - if totalNeuronCount == 0 || len(links) == 0 || len(activations) != totalNeuronCount { - message := fmt.Sprintf("failed to create network solver: links [%d], nodes [%d], activations [%d]", - len(links), totalNeuronCount, len(activations)) + if totalNeuronCount == 0 || (!useLeo && len(links) == 0) || len(activations) != totalNeuronCount { + message := fmt.Sprintf("failed to create network solver: links [%d], nodes [%d], activations [%d], LEO [%t]", + len(links), totalNeuronCount, len(activations), useLeo) return nil, errors.New(message) } solver := network.NewFastModularNetworkSolver( diff --git a/cppn/evolvable_substrate_layout.go b/cppn/evolvable_substrate_layout.go index 0f3d4f2..8a28d0f 100644 --- a/cppn/evolvable_substrate_layout.go +++ b/cppn/evolvable_substrate_layout.go @@ -3,7 +3,7 @@ package cppn import ( "fmt" "github.com/pkg/errors" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/network" ) // EvolvableSubstrateLayout Defines layout of neurons in the substrate diff --git a/cppn/evolvable_substrate_layout_test.go b/cppn/evolvable_substrate_layout_test.go index 02d09eb..2a7d1cc 100644 --- a/cppn/evolvable_substrate_layout_test.go +++ b/cppn/evolvable_substrate_layout_test.go @@ -3,7 +3,7 @@ package cppn import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/network" "testing" ) diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index 57aee8f..4a461e1 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -4,7 +4,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" - "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v4/neat/math" "testing" ) diff --git a/cppn/substrate.go b/cppn/substrate.go index b3217f7..8b4cd0d 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -4,8 +4,8 @@ import ( "errors" "fmt" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" - neatmath "github.com/yaricom/goNEAT/v3/neat/math" - "github.com/yaricom/goNEAT/v3/neat/network" + neatmath "github.com/yaricom/goNEAT/v4/neat/math" + "github.com/yaricom/goNEAT/v4/neat/network" "math" ) diff --git a/cppn/substrate_graph_builder.go b/cppn/substrate_graph_builder.go index 179badd..c2a8a81 100644 --- a/cppn/substrate_graph_builder.go +++ b/cppn/substrate_graph_builder.go @@ -3,8 +3,8 @@ package cppn import ( "errors" "github.com/yaricom/goGraphML/graphml" - "github.com/yaricom/goNEAT/v3/neat/math" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/math" + "github.com/yaricom/goNEAT/v4/neat/network" "io" ) diff --git a/cppn/substrate_graph_builder_test.go b/cppn/substrate_graph_builder_test.go index c38da54..faf03b7 100644 --- a/cppn/substrate_graph_builder_test.go +++ b/cppn/substrate_graph_builder_test.go @@ -4,8 +4,8 @@ import ( "bytes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v3/neat/math" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/math" + "github.com/yaricom/goNEAT/v4/neat/network" "strings" "testing" ) diff --git a/cppn/substrate_layout.go b/cppn/substrate_layout.go index 0daf446..3ccb2e8 100644 --- a/cppn/substrate_layout.go +++ b/cppn/substrate_layout.go @@ -3,7 +3,7 @@ package cppn import ( "errors" "fmt" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/network" ) // SubstrateLayout Defines layout of neurons in the substrate diff --git a/cppn/substrate_layout_test.go b/cppn/substrate_layout_test.go index 82020a3..1cca679 100644 --- a/cppn/substrate_layout_test.go +++ b/cppn/substrate_layout_test.go @@ -2,7 +2,7 @@ package cppn import ( "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/neat/network" "testing" ) diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 99d9dc4..e25a33c 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yaricom/goESHyperNEAT/v2/hyperneat" - "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v4/neat/math" "testing" ) diff --git a/data/retina/cppn_genome.yml b/data/retina/cppn_genome.yml index cd58b02..27d81d0 100644 --- a/data/retina/cppn_genome.yml +++ b/data/retina/cppn_genome.yml @@ -14,12 +14,12 @@ genome: # The neuron nodes for this genome nodes: - - { id: 1, trait_id: 0, type: BIAS, activation: NullActivation } + - { id: 1, trait_id: 0, type: BIAS, activation: LinearActivation } # The input nodes - sensors - - { id: 2, trait_id: 0, type: INPT, activation: NullActivation } # x1 - - { id: 3, trait_id: 0, type: INPT, activation: NullActivation } # y1 - - { id: 4, trait_id: 0, type: INPT, activation: NullActivation } # x2 - - { id: 5, trait_id: 0, type: INPT, activation: NullActivation } # y2 + - { id: 2, trait_id: 0, type: INPT, activation: LinearActivation } # x1 + - { id: 3, trait_id: 0, type: INPT, activation: LinearActivation } # y1 + - { id: 4, trait_id: 0, type: INPT, activation: LinearActivation } # x2 + - { id: 5, trait_id: 0, type: INPT, activation: LinearActivation } # y2 # The output nodes - actuators - { id: 6, trait_id: 0, type: OUTP, activation: TanhActivation } - { id: 7, trait_id: 0, type: OUTP, activation: StepActivation } # LEO @@ -31,9 +31,11 @@ genome: genes: - { src_id: 1, tgt_id: 9, weight: 0.5, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true } - { src_id: 1, tgt_id: 7, weight: 0.5, trait_id: 6, innov_num: 2, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 2, tgt_id: 8, weight: 0.5, trait_id: 2, innov_num: 6, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 3, tgt_id: 9, weight: 0.5, trait_id: 3, innov_num: 7, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 4, tgt_id: 8, weight: 0.5, trait_id: 4, innov_num: 8, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 5, tgt_id: 9, weight: 0.5, trait_id: 5, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 8, tgt_id: 7, weight: 0.5, trait_id: 7, innov_num: 4, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 9, tgt_id: 6, weight: 0.5, trait_id: 8, innov_num: 5, mut_num: 0, recurrent: false, enabled: true } \ No newline at end of file + - { src_id: 2, tgt_id: 8, weight: 0.5, trait_id: 2, innov_num: 3, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 3, tgt_id: 9, weight: 0.5, trait_id: 3, innov_num: 4, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 4, tgt_id: 8, weight: 0.5, trait_id: 4, innov_num: 5, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 5, tgt_id: 9, weight: 0.5, trait_id: 5, innov_num: 6, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 8, tgt_id: 6, weight: 0.5, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 8, tgt_id: 7, weight: 0.5, trait_id: 7, innov_num: 8, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 9, tgt_id: 6, weight: 0.5, trait_id: 8, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 9, tgt_id: 7, weight: 0.5, trait_id: 8, innov_num: 10, mut_num: 0, recurrent: false, enabled: true } \ No newline at end of file diff --git a/data/retina/es_hyper.neat.yml b/data/retina/es_hyper.neat.yml index 9563fba..d6012cd 100644 --- a/data/retina/es_hyper.neat.yml +++ b/data/retina/es_hyper.neat.yml @@ -18,11 +18,11 @@ excess_coeff: 1.0 mutdiff_coeff: 0.4 # This global tells compatibility threshold under which two Genomes are considered the same species -compat_threshold: 4.0 +compat_threshold: 3.0 # How much does age matter? Gives a fitness boost up to some young age (niche). If it is 1, then young species get no fitness boost. -age_significance: 1.1 +age_significance: 1.2 # Percent of average fitness for survival, how many get to reproduce based on survival_thresh * pop_size -survival_thresh: 0.2 +survival_thresh: 0.3 # Probabilities of a non-mating reproduction mutate_only_prob: 0.25 @@ -33,15 +33,15 @@ mutate_link_trait_prob: 0.1 # Probability of node trait mutation mutate_node_trait_prob: 0.1 # Probability of link weight value mutation -mutate_link_weights_prob: 0.9 +mutate_link_weights_prob: 0.5 # Probability of enabling/disabling of specific link/gene -mutate_toggle_enable_prob: 0.0 +mutate_toggle_enable_prob: 0.02 # Probability of finding the first disabled gene and re-enabling it mutate_gene_reenable_prob: 0.0 # Probability of adding new node mutate_add_node_prob: 0.03 # Probability of adding new link between nodes -mutate_add_link_prob: 0.1 +mutate_add_link_prob: 0.03 # Probability of making connections from disconnected sensors (input, bias type neurons) mutate_connect_sensors: 0.5 @@ -60,26 +60,26 @@ mate_multipoint_avg_prob: 0.3 mate_singlepoint_prob: 0.3 # Probability of mating without mutation -mate_only_prob: 0.2 +mate_only_prob: 0.25 # Probability of forcing selection of ONLY links that are naturally recurrent recur_only_prob: 0.0 # The number of babies to stolen off to the champions -babies_stolen: 0 +babies_stolen: 5 # The population size as a number of organisms pop_size: 300 # Age when Species starts to be penalized -dropoff_age: 50 +dropoff_age: 35 # Number of tries mutate_add_link will attempt to find an open link newlink_tries: 50 # Tells to print population to file every n generations print_every: 10 # The number of runs to average over in an experiment -num_runs: 100 +num_runs: 1 # The number of epochs (generations) to execute training -num_generations: 100 +num_generations: 1500 # The epoch's executor type to apply [sequential, parallel] epoch_executor: sequential @@ -92,12 +92,9 @@ log_level: info # The nodes activation functions list to choose from (activation function -> it's selection probability) node_activators: - - SigmoidBipolarActivation 0.15 - - SigmoidSteepenedActivation 0.15 - - GaussianBipolarActivation 0.15 - - LinearAbsActivation 0.15 - - SineActivation 0.15 - - TanhActivation 0.25 + - GaussianBipolarActivation 0.4 + - SineActivation 0.3 + - LinearAbsActivation 0.3 ######################################## # The HyperNEAT specific configuration # @@ -109,7 +106,10 @@ link_threshold: 0.2 weight_range: 3 # The substrate activation function, determines which activation function each node in the substrate will have. -substrate_activator: SigmoidBipolarActivation +substrate_activator: SigmoidPlainActivation + +# The BIAS value of the CPPN network if appropriate [default: 1.0] +cppn_bias: 0.33 ########################################### # The ES-HyperNEAT specific configuration # @@ -120,11 +120,11 @@ initial_depth: 2 maximal_depth: 3 # DivisionThreshold defines the substrate division threshold. -division_threshold: 0.5 +division_threshold: 0.3 # VarianceThreshold defines the variance threshold for the initial sampling. variance_threshold: 0.03 # BandingThreshold defines the threshold that determines when points are regarded to be in a band. -banding_threshold: 0.3 +banding_threshold: 0.2 # ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. es_iterations: 1 diff --git a/eshyperneat/es_hyper_neat_test.go b/eshyperneat/es_hyper_neat_test.go index c3bd997..dc3a694 100644 --- a/eshyperneat/es_hyper_neat_test.go +++ b/eshyperneat/es_hyper_neat_test.go @@ -3,7 +3,7 @@ package eshyperneat import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v4/neat/math" "os" "testing" ) diff --git a/examples/common.go b/examples/common.go index 5d08194..e1c7d87 100644 --- a/examples/common.go +++ b/examples/common.go @@ -1,6 +1,6 @@ package examples -import "github.com/yaricom/goNEAT/v3/neat" +import "github.com/yaricom/goNEAT/v4/neat" const ( compatibilityThresholdStep = 0.1 diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 0df48d6..3b4b272 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -8,11 +8,11 @@ import ( "github.com/yaricom/goESHyperNEAT/v2/cppn" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "github.com/yaricom/goESHyperNEAT/v2/examples" - "github.com/yaricom/goNEAT/v3/experiment" - "github.com/yaricom/goNEAT/v3/experiment/utils" - "github.com/yaricom/goNEAT/v3/neat" - "github.com/yaricom/goNEAT/v3/neat/genetics" - "github.com/yaricom/goNEAT/v3/neat/network" + "github.com/yaricom/goNEAT/v4/experiment" + "github.com/yaricom/goNEAT/v4/experiment/utils" + "github.com/yaricom/goNEAT/v4/neat" + "github.com/yaricom/goNEAT/v4/neat/genetics" + "github.com/yaricom/goNEAT/v4/neat/network" "math" "os" ) @@ -89,8 +89,13 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population if organism.Fitness > maxPopulationFitness { maxPopulationFitness = organism.Fitness - bestLinkCount = organism.Phenotype.LinkCount() - bestNodeCount = organism.Phenotype.NodeCount() + if phenotype, err := organism.Phenotype(); err == nil { + bestLinkCount = phenotype.LinkCount() + bestNodeCount = phenotype.NodeCount() + } else { + neat.ErrorLog(fmt.Sprintf("Failed to get organism Phenotype, reason: %s", err)) + return err + } bestSubstrateSolver = solver } @@ -102,7 +107,7 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population epoch.Champion = organism if epoch.WinnerNodes == 9 { // You could dump out optimal genomes here if desired - if optPath, err := utils.WriteGenomePlain("xor_optimal", e.outDir, organism, epoch); err != nil { + if optPath, err := utils.WriteGenomePlain("retina_optimal", e.outDir, organism, epoch); err != nil { neat.ErrorLog(fmt.Sprintf("Failed to dump optimal genome, reason: %s\n", err)) } else { neat.InfoLog(fmt.Sprintf("Dumped optimal genome to: %s\n", optPath)) @@ -125,9 +130,7 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population if epoch.Solved { // print winner organism org := epoch.Champion - if depth, err := org.Phenotype.MaxActivationDepthFast(0); err == nil { - neat.InfoLog(fmt.Sprintf("Activation depth of the winner: %d\n", depth)) - } + utils.PrintActivationDepth(org, true) genomeFile := "retina_cppn_winner" // Prints the winner organism's Genome to the file! @@ -157,17 +160,21 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population // adjust species count by keeping it constant examples.AdjustSpeciesNumber(speciesCount, epoch.Id, e.compatAdjustFreq, e.numSpeciesTarget, options) + bestSolverLinks, bestSolverNodes := -1, -1 + if bestSubstrateSolver != nil { + bestSolverLinks, bestSolverNodes = bestSubstrateSolver.LinkCount(), bestSubstrateSolver.NodeCount() + } + neat.InfoLog( fmt.Sprintf("%d species -> %d organisms [compatibility threshold: %.1f, target: %d]\nbest CPNN organism [fitness: %.2f, links: %d, nodes: %d], best solver [links: %d, nodes: %d]", speciesCount, len(population.Organisms), options.CompatThreshold, e.numSpeciesTarget, - maxPopulationFitness, bestLinkCount, bestNodeCount, - bestSubstrateSolver.LinkCount(), bestSubstrateSolver.NodeCount())) + maxPopulationFitness, bestLinkCount, bestNodeCount, bestSolverLinks, bestSolverNodes)) } return nil } -// organismEvaluate evaluates an individual phenotype network with retina experiment and returns true if its won -func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *genetics.Organism) (bool, network.Solver, error) { +// organismEvaluate evaluates an individual phenotype network with retina experiment and returns true if it's a winner +func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *genetics.Organism) (bool, network.Solver, error) { options, ok := eshyperneat.FromContext(ctx) if !ok { return false, nil, eshyperneat.ErrESHyperNEATOptionsNotFound @@ -177,7 +184,10 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen //if err != nil { // return false, err //} - cppnSolver := organism.Phenotype + cppnSolver, err := organism.Phenotype() + if err != nil { + return false, nil, err + } // create substrate layout inputCount := e.env.inputSize * 2 // left + right pixels of visual object @@ -190,7 +200,7 @@ func (e generationEvaluator) organismEvaluate(ctx context.Context, organism *gen graph := cppn.NewSubstrateGraphMLBuilder("retina ES-HyperNEAT", false) solver, err := substr.CreateNetworkSolver(cppnSolver, e.useLeo, graph, options) if err != nil { - return false, nil, err + return false, nil, errors.Wrap(err, fmt.Sprintf("failed to evaluate organism: %s", organism)) } // Evaluate the detector ANN against 256 combinations of the left and the right visual objects diff --git a/executor.go b/executor.go index b67886d..3ebefa3 100644 --- a/executor.go +++ b/executor.go @@ -6,9 +6,9 @@ import ( "fmt" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" "github.com/yaricom/goESHyperNEAT/v2/examples/retina" - "github.com/yaricom/goNEAT/v3/experiment" - "github.com/yaricom/goNEAT/v3/neat" - "github.com/yaricom/goNEAT/v3/neat/genetics" + "github.com/yaricom/goNEAT/v4/experiment" + "github.com/yaricom/goNEAT/v4/neat" + "github.com/yaricom/goNEAT/v4/neat/genetics" "log" "math/rand" "os" @@ -29,13 +29,16 @@ func main() { var logLevel = flag.String("log-level", "", "The logger level to be used. Overrides the one set in configuration.") var trialsCount = flag.Int("trials", 0, "The number of trials for experiment. Overrides the one set in configuration.") + var seed = flag.Int64("seed", -1, "The seed for the random number generator [-1 to use current Unix timestamp].") flag.Parse() // Seed the random-number generator with current time so that // the numbers will be different every time we run. - seed := time.Now().Unix() - rand.Seed(seed) + if *seed < 0 { + *seed = time.Now().UnixNano() + } + rand.Seed(*seed) // Load context configuration neatOptions, err := neat.ReadNeatOptionsFromFile(*contextPath) @@ -87,7 +90,7 @@ func main() { exp := experiment.Experiment{ Id: 0, Trials: make(experiment.Trials, neatOptions.NumRuns), - RandSeed: seed, + RandSeed: *seed, } var generationEvaluator experiment.GenerationEvaluator var trialObserver experiment.TrialRunObserver diff --git a/go.mod b/go.mod index 0e64723..cf2c0a4 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,20 @@ module github.com/yaricom/goESHyperNEAT/v2 -go 1.17 +go 1.21 require ( github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.7.1 - github.com/yaricom/goGraphML v1.1.0 - github.com/yaricom/goNEAT/v3 v3.0.3 + github.com/stretchr/testify v1.9.0 + github.com/yaricom/goGraphML v1.4.1 + github.com/yaricom/goNEAT/v4 v4.0.2 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sbinet/npyio v0.6.0 // indirect - github.com/spf13/cast v1.5.0 // indirect - golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 // indirect - gonum.org/v1/gonum v0.11.0 // indirect + github.com/sbinet/npyio v0.8.0 // indirect + github.com/spf13/cast v1.5.1 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + gonum.org/v1/gonum v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index d62e953..4408f0d 100644 --- a/go.sum +++ b/go.sum @@ -1,96 +1,44 @@ -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/sbinet/npyio v0.6.0 h1:IyqqQIzRjDym9xnIXsToCKei/qCzxDP+Y74KoMlMgXo= -github.com/sbinet/npyio v0.6.0/go.mod h1:/q3BNr6dJOy+t6h7RZchTJ0nwRJO52mivaem29WE1j8= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sbinet/npyio v0.8.0 h1:n+jtLFIjcJNENOI44lG7BUwWFqtgdQAerqyXDtC956A= +github.com/sbinet/npyio v0.8.0/go.mod h1:26fj1nEFY78AYqkANkcSw4dYwLHXFWFehgOSu5HKwgw= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yaricom/goGraphML v1.1.0 h1:CrM6yGmZ8Azv2Id2KIzei277MPe5YFzKkOOJu45uOBM= -github.com/yaricom/goGraphML v1.1.0/go.mod h1:OM0MGAy6tdufwNYPW9BS2mR6NMArD7RtlakyTs+A3Vk= -github.com/yaricom/goNEAT/v3 v3.0.2 h1:w70mMJPRTTo98tAL1s42H7yQ0pylMP6fZhxFdxGD9bo= -github.com/yaricom/goNEAT/v3 v3.0.2/go.mod h1:gkZxHLDW3dRwacNk0ITgt9okkCSnFeTbosWfLFnYucg= -github.com/yaricom/goNEAT/v3 v3.0.3 h1:T7gHNw2l/CfCkC4ebettHekW4z9ztBSnWHMwrhG1Y3I= -github.com/yaricom/goNEAT/v3 v3.0.3/go.mod h1:gkZxHLDW3dRwacNk0ITgt9okkCSnFeTbosWfLFnYucg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yaricom/goGraphML v1.4.1 h1:ZT2yWzKeVx8dbiOZT+tBUbmCwDfWAG70HjavSWajTeY= +github.com/yaricom/goGraphML v1.4.1/go.mod h1:WdO/4yppeN4Ly/dnr5/Z+d/1UoQ8SA8r+x0IQ6/jBbE= +github.com/yaricom/goNEAT/v4 v4.0.2 h1:+XtgXI377ouYXWSRJGgFCoXUTeENSogdnxYq5USq2fQ= +github.com/yaricom/goNEAT/v4 v4.0.2/go.mod h1:/tekyUuX/P14WrL5aL/kgbDE4rB/rRJtpCS8OSp4UB4= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index cda8c1c..22159ca 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -3,7 +3,7 @@ package hyperneat import ( "github.com/pkg/errors" - "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v4/neat/math" "gopkg.in/yaml.v3" "io" "io/ioutil" diff --git a/hyperneat/hyper_neat_test.go b/hyperneat/hyper_neat_test.go index 456f1bf..5060dce 100644 --- a/hyperneat/hyper_neat_test.go +++ b/hyperneat/hyper_neat_test.go @@ -3,7 +3,7 @@ package hyperneat import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/yaricom/goNEAT/v3/neat/math" + "github.com/yaricom/goNEAT/v4/neat/math" "os" "testing" ) From 0afe4bb68e6f869be178e502bcb162630dac54f1 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 15 Apr 2024 21:22:38 +0300 Subject: [PATCH 37/57] Added new configuration options --- data/retina/cppn_genome.yml | 6 +++--- data/retina/es_hyper.neat.yml | 34 +++++++++++++++++++++------------- eshyperneat/es_hyper_neat.go | 5 +++++ hyperneat/hyper_neat.go | 3 +++ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/data/retina/cppn_genome.yml b/data/retina/cppn_genome.yml index 27d81d0..a948df1 100644 --- a/data/retina/cppn_genome.yml +++ b/data/retina/cppn_genome.yml @@ -21,11 +21,11 @@ genome: - { id: 4, trait_id: 0, type: INPT, activation: LinearActivation } # x2 - { id: 5, trait_id: 0, type: INPT, activation: LinearActivation } # y2 # The output nodes - actuators - - { id: 6, trait_id: 0, type: OUTP, activation: TanhActivation } + - { id: 6, trait_id: 0, type: OUTP, activation: LinearActivation } - { id: 7, trait_id: 0, type: OUTP, activation: StepActivation } # LEO # The hidden node - - { id: 8, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G1 - - { id: 9, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G2 + - { id: 8, trait_id: 0, type: HIDN, activation: SigmoidSteepenedActivation } # G1 + - { id: 9, trait_id: 0, type: HIDN, activation: SigmoidSteepenedActivation } # G2 # The genes - connection between neuron nodes within this genome genes: diff --git a/data/retina/es_hyper.neat.yml b/data/retina/es_hyper.neat.yml index d6012cd..8cf7c29 100644 --- a/data/retina/es_hyper.neat.yml +++ b/data/retina/es_hyper.neat.yml @@ -18,7 +18,7 @@ excess_coeff: 1.0 mutdiff_coeff: 0.4 # This global tells compatibility threshold under which two Genomes are considered the same species -compat_threshold: 3.0 +compat_threshold: 6.0 # How much does age matter? Gives a fitness boost up to some young age (niche). If it is 1, then young species get no fitness boost. age_significance: 1.2 # Percent of average fitness for survival, how many get to reproduce based on survival_thresh * pop_size @@ -39,9 +39,9 @@ mutate_toggle_enable_prob: 0.02 # Probability of finding the first disabled gene and re-enabling it mutate_gene_reenable_prob: 0.0 # Probability of adding new node -mutate_add_node_prob: 0.03 +mutate_add_node_prob: 0.1 # Probability of adding new link between nodes -mutate_add_link_prob: 0.03 +mutate_add_link_prob: 0.5 # Probability of making connections from disconnected sensors (input, bias type neurons) mutate_connect_sensors: 0.5 @@ -79,7 +79,7 @@ print_every: 10 # The number of runs to average over in an experiment num_runs: 1 # The number of epochs (generations) to execute training -num_generations: 1500 +num_generations: 100 #1500 # The epoch's executor type to apply [sequential, parallel] epoch_executor: sequential @@ -92,18 +92,21 @@ log_level: info # The nodes activation functions list to choose from (activation function -> it's selection probability) node_activators: - - GaussianBipolarActivation 0.4 - - SineActivation 0.3 - - LinearAbsActivation 0.3 + - SigmoidSteepenedActivation 1.0 + - SineActivation 1.0 + - LinearAbsActivation 1.0 + - GaussianBipolarActivation 1.0 ######################################## # The HyperNEAT specific configuration # ######################################## # The threshold value to indicate which links should be included -link_threshold: 0.2 +link_threshold: 0.1 +# Threshold above which a connection is expressed if Link Expression Output (LEO) enabled +leo_threshold: 0.5 # The weight range defines the minimum and maximum values for weights on substrate connections -weight_range: 3 +weight_range: 1 # The substrate activation function, determines which activation function each node in the substrate will have. substrate_activator: SigmoidPlainActivation @@ -115,16 +118,21 @@ cppn_bias: 0.33 # The ES-HyperNEAT specific configuration # ########################################### # InitialDepth defines the initial ES-HyperNEAT sample resolution. -initial_depth: 2 +initial_depth: 1 # Maximal ES-HyperNEAT sample resolution if the variance is still higher than the given division threshold maximal_depth: 3 # DivisionThreshold defines the substrate division threshold. -division_threshold: 0.3 +division_threshold: 0.5 # VarianceThreshold defines the variance threshold for the initial sampling. -variance_threshold: 0.03 +variance_threshold: 0.1 # BandingThreshold defines the threshold that determines when points are regarded to be in a band. -banding_threshold: 0.2 +banding_threshold: 0.5 + +# Quadtree Dimensions +# The range of the tree. Typically set to 2.0 +width: 2.0 +height: 2.0 # ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. es_iterations: 1 diff --git a/eshyperneat/es_hyper_neat.go b/eshyperneat/es_hyper_neat.go index f9a88c5..f334306 100644 --- a/eshyperneat/es_hyper_neat.go +++ b/eshyperneat/es_hyper_neat.go @@ -33,6 +33,11 @@ type Options struct { // The bigger this value the fewer connections/hidden nodes will be added, i.e. wide bands approximation. BandingThreshold float64 `yaml:"banding_threshold"` + // Quadtree Dimensions + // The range of the tree. Typically set to 2.0 + Width float64 `yaml:"width"` + Height float64 `yaml:"height"` + // ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. ESIterations int `yaml:"es_iterations"` } diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index 22159ca..46c1f61 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -22,6 +22,9 @@ type Options struct { // from -WeightRange to +WeightRange, and can be any integer WeightRange float64 `yaml:"weight_range"` + // LeoThreshold Threshold above which a connection is expressed if Link Expression Output (LEO) enabled + LeoThreshold float64 `yaml:"leo_threshold"` + // The substrate activation function SubstrateActivator SubstrateActivatorType `yaml:"substrate_activator"` // The BIAS value for CPPN network From a8b5f7a4c706e12f3c1cf2a1d2d73de1d74b483d Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 15 Apr 2024 21:22:58 +0300 Subject: [PATCH 38/57] Removed obsolete --- cppn/substrate_layout.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cppn/substrate_layout.go b/cppn/substrate_layout.go index 3ccb2e8..96011b9 100644 --- a/cppn/substrate_layout.go +++ b/cppn/substrate_layout.go @@ -90,8 +90,6 @@ func (g *GridSubstrateLayout) NodePosition(index int, nType network.NodeNeuronTy if index >= count { return nil, errors.New("neuron index is out of range") - } else if nType == network.BiasNeuron { - return &point, nil } // calculate X position point.X = -1.0 + delta/2.0 // the initial position with half delta shift From fc50d1dc29316bf43d74af559c2cadfb90b36081 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 15 Apr 2024 21:23:24 +0300 Subject: [PATCH 39/57] Implemented support for LEO threshold --- cppn/substrate.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cppn/substrate.go b/cppn/substrate.go index 8b4cd0d..729a218 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -257,10 +257,10 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB func fastNetworkLink(coordinates []float64, cppn network.Solver, useLeo bool, source, target int, options *hyperneat.Options) (*network.FastNetworkLink, error) { if outs, err := queryCPPN(coordinates, cppn); err != nil { return nil, err - } else if useLeo && outs[1] > 0 { + } else if useLeo && outs[1] >= options.LeoThreshold { // add links only when CPPN LEO output signals to return createLink(outs[0], source, target, options.WeightRange), nil - } else if !useLeo && math.Abs(outs[0]) > options.LinkThreshold { + } else if !useLeo && math.Abs(outs[0]) >= options.LinkThreshold { // add only links with signal exceeding provided threshold return createThresholdNormalizedLink(outs[0], source, target, options.LinkThreshold, options.WeightRange), nil } From 5fa5fddb2d4252a5d33fe72a64d04f951413c246 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 15 Apr 2024 21:24:01 +0300 Subject: [PATCH 40/57] Changed variance calculation to be more optimized --- cppn/cppn.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/cppn/cppn.go b/cppn/cppn.go index d455dcd..69d88d3 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -6,6 +6,7 @@ import ( "errors" "github.com/yaricom/goNEAT/v4/neat/genetics" "github.com/yaricom/goNEAT/v4/neat/network" + "gonum.org/v1/gonum/stat" "math" ) @@ -79,20 +80,9 @@ func nodeVariance(node *QuadNode) float64 { return 0.0 } - cppnValues := nodeCPPNValues(node) - // calculate median and variance - meanW, variance := 0.0, 0.0 - for _, w := range cppnValues { - meanW += w - } - meanW /= float64(len(cppnValues)) - - for _, w := range cppnValues { - variance += math.Pow(w-meanW, 2) - } - variance /= float64(len(cppnValues)) - - return variance + // node always has four child nodes if any + cppnValues := []float64{node.Nodes[0].Weight(), node.Nodes[1].Weight(), node.Nodes[2].Weight(), node.Nodes[3].Weight()} + return stat.Variance(cppnValues, nil) } // Collects the CPPN values stored in a given quadtree node From 2b9762768eb36793010b6bbd8fb45f8e88f1eb3b Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 15 Apr 2024 21:24:30 +0300 Subject: [PATCH 41/57] Added support of Z coordinate by QuadTree --- cppn/quad_tree.go | 58 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/cppn/quad_tree.go b/cppn/quad_tree.go index 9141a94..51b25b3 100644 --- a/cppn/quad_tree.go +++ b/cppn/quad_tree.go @@ -4,7 +4,7 @@ import "fmt" // PointF Defines point with float precision coordinates type PointF struct { - X, Y float64 + X, Y, Z float64 } func NewPointF(x, y float64) *PointF { @@ -18,20 +18,18 @@ func (p *PointF) String() string { // QuadPoint Defines the quad-point in the 4 dimensional hypercube type QuadPoint struct { // The associated coordinates - X1, X2, Y1, Y2 float64 - // The CPPN outputs for this point - CppnOut []float64 -} - -func (q *QuadPoint) Weight() float64 { - return q.CppnOut[0] + X1, X2, Y1, Y2, Z1, Z2 float64 + // Weight + Weight float64 + // Leo + Leo float64 } func (q *QuadPoint) String() string { - str := fmt.Sprintf("((%f, %f),(%f, %f)) = %f", q.X1, q.Y1, q.X2, q.Y2, q.Weight()) - if len(q.CppnOut) > 1 { + str := fmt.Sprintf("((%f, %f),(%f, %f)) = %f", q.X1, q.Y1, q.X2, q.Y2, q.Weight) + if q.Leo >= 0 { var status string - if q.CppnOut[1] > 0 { + if q.Leo > 0 { status = "enabled" } else { status = "disabled" @@ -43,17 +41,17 @@ func (q *QuadPoint) String() string { // NewQuadPoint Creates new quad point func NewQuadPoint(x1, y1, x2, y2 float64, node *QuadNode) *QuadPoint { - outs := make([]float64, len(node.CppnOut)) - copy(outs, node.CppnOut) - return &QuadPoint{X1: x1, Y1: y1, X2: x2, Y2: y2, CppnOut: outs} + return &QuadPoint{X1: x1, Y1: y1, X2: x2, Y2: y2, Weight: node.Weight(), Leo: node.Leo()} } // QuadNode Defines quad-tree node to model 4 dimensional hypercube type QuadNode struct { // The coordinates of center of this quad-tree node's square - X, Y float64 + X, Y, Z float64 // The width of this quad-tree node's square Width float64 + // The height of this quad-tree node's square + Height float64 // The CPPN outputs for this node CppnOut []float64 @@ -68,16 +66,42 @@ func (q *QuadNode) Weight() float64 { return q.CppnOut[0] } +func (q *QuadNode) Leo() float64 { + if len(q.CppnOut) > 1 { + return q.CppnOut[1] + } + return -1.0 +} + +func (q *QuadNode) HasLeo() bool { + return len(q.CppnOut) > 1 +} + func (q *QuadNode) String() string { - return fmt.Sprintf("((%f, %f), %f) = %f at %d", q.X, q.Y, q.Width, q.CppnOut, q.Level) + return fmt.Sprintf("((%f, %f), %f x %f) = %f at %d", q.X, q.Y, q.Width, q.Height, q.CppnOut, q.Level) } // NewQuadNode Creates new quad-node with given parameters -func NewQuadNode(x, y, width float64, level int) *QuadNode { +func NewQuadNode(x, y, width, height float64, level int) *QuadNode { + node := QuadNode{ + X: x, + Y: y, + Width: width, + Height: height, + CppnOut: []float64{0.0}, + Level: level, + } + return &node +} + +// NewQuadNodeZ Creates new quad-node with given parameters with Z coordinate value +func NewQuadNodeZ(x, y, z, width, height float64, level int) *QuadNode { node := QuadNode{ X: x, Y: y, + Z: z, Width: width, + Height: height, CppnOut: []float64{0.0}, Level: level, } From 54e4e0263acc203ebc418cd13ce18872b5895cad Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 18 Apr 2024 18:56:30 +0300 Subject: [PATCH 42/57] Fixed evolvable substrate generation. Fixed quad tree stringer. Fixed ES tests --- cppn/cppn_test.go | 40 +++---- cppn/evolvable_substrate.go | 104 ++++++++++--------- cppn/evolvable_substrate_test.go | 9 +- cppn/quad_tree.go | 10 +- data/test/test_cppn_hyperneat_genome.yml | 21 ++-- data/test/test_cppn_leo_hyperneat_genome.yml | 41 +++++--- data/test/test_es_hyper.neat.yml | 10 +- 7 files changed, 129 insertions(+), 106 deletions(-) diff --git a/cppn/cppn_test.go b/cppn/cppn_test.go index da3a144..54d52a1 100644 --- a/cppn/cppn_test.go +++ b/cppn/cppn_test.go @@ -16,7 +16,7 @@ func TestQuadNode_NodeVariance(t *testing.T) { // get variance and check results variance := nodeVariance(root) - assert.InDelta(t, 3.3877551020408165, variance, 1e-16) + assert.InDelta(t, 1.6666666666666667, variance, 1e-16) } func TestQuadNode_nodeCPPNValues(t *testing.T) { @@ -26,7 +26,7 @@ func TestQuadNode_nodeCPPNValues(t *testing.T) { vals := nodeCPPNValues(root) require.Len(t, vals, 7, "wrong node values length") - expected := []float64{0, 1, 2, 3, 2, 4, 6} + expected := []float64{0, 1, 2, 3, 1, 2, 3} assert.ElementsMatch(t, expected, vals) } @@ -34,51 +34,51 @@ func TestFastSolverFromGenomeFile(t *testing.T) { cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read genome file") require.NotNil(t, cppn, "CPPN expected") - require.Equal(t, 7, cppn.NodeCount(), "wrong nodes number") - require.Equal(t, 7, cppn.LinkCount(), "wrong links number") + require.Equal(t, 9, cppn.NodeCount(), "wrong nodes number") + require.Equal(t, 8, cppn.LinkCount(), "wrong links number") // test query - coords := []float64{0.0, 0.0, 0.5, 0.5} + coords := []float64{0.0, 0.0, 0.0, 0.5, 0.5, 0.0} outs, err := queryCPPN(coords, cppn) require.NoError(t, err, "failed to query CPPN") require.NotNil(t, outs, "output expected") require.Len(t, outs, 1) - assert.InDelta(t, 0.4864161653290716, outs[0], 1e-16, "wrong output value") + assert.InDelta(t, 0.5109358170373398, outs[0], 1e-16, "wrong output value") } func TestFastSolverFromGenomeFile_LEO(t *testing.T) { cppn, err := FastSolverFromGenomeFile(cppnLeoHyperNEATTestGenomePath) require.NoError(t, err, "failed to read genome file") require.NotNil(t, cppn, "CPPN expected") - require.Equal(t, 9, cppn.NodeCount(), "wrong nodes number") - require.Equal(t, 11, cppn.LinkCount(), "wrong links number") + require.Equal(t, 11, cppn.NodeCount(), "wrong nodes number") + require.Equal(t, 15, cppn.LinkCount(), "wrong links number") // test query - coords := []float64{0.0, 0.0, 0.5, 0.5} + coords := []float64{0.0, 0.0, 0.0, 0.5, 0.5, 0.0} outs, err := queryCPPN(coords, cppn) require.NoError(t, err, "failed to query CPPN") require.NotNil(t, outs, "output expected") require.Len(t, outs, 2) t.Log(outs) - assert.InDelta(t, 0.4117980729033359, outs[0], 1e-16, "wrong output value") + assert.InDelta(t, 0.15114695049520718, outs[0], 1e-16, "wrong output value") assert.Equal(t, 1.0, outs[1], "wrong LEO value") } func buildTree() *QuadNode { - root := NewQuadNode(0, 0, 1, 1) + root := NewQuadNode(0, 0, 1, 1, 1) root.Nodes = []*QuadNode{ - NewQuadNode(-1, 1, 0.5, 2), - NewQuadNode(-1, -1, 0.5, 2), - NewQuadNode(1, 1, 0.5, 2), - NewQuadNode(1, -1, 0.5, 2), + NewQuadNode(-1, 1, 0.5, 0.5, 2), + NewQuadNode(-1, -1, 0.5, 0.5, 2), + NewQuadNode(1, 1, 0.5, 0.5, 2), + NewQuadNode(1, -1, 0.5, 0.5, 2), } - fillW(root.Nodes, 2.0) + fillW(root.Nodes, 1.0) root.Nodes[0].Nodes = []*QuadNode{ - NewQuadNode(-1, 1, 0.5, 3), - NewQuadNode(-1, -1, 0.5, 3), - NewQuadNode(1, 1, 0.5, 3), - NewQuadNode(1, -1, 0.5, 3), + NewQuadNode(-1, 1, 0.5, 0.5, 3), + NewQuadNode(-1, -1, 0.5, 0.5, 3), + NewQuadNode(1, 1, 0.5, 0.5, 3), + NewQuadNode(1, -1, 0.5, 0.5, 3), } fillW(root.Nodes[0].Nodes, 1.0) return root diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index 332f3e7..a3061d8 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -2,8 +2,8 @@ package cppn import ( "container/list" - "errors" "fmt" + "github.com/pkg/errors" "github.com/yaricom/goESHyperNEAT/v2/eshyperneat" neatmath "github.com/yaricom/goNEAT/v4/neat/math" "github.com/yaricom/goNEAT/v4/neat/network" @@ -30,7 +30,7 @@ type EvolvableSubstrate struct { // NewEvolvableSubstrate Creates new instance of evolvable substrate func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation neatmath.NodeActivationType) *EvolvableSubstrate { return &EvolvableSubstrate{ - coords: make([]float64, 4), + coords: make([]float64, 6), Layout: layout, NodesActivation: nodesActivation, } @@ -39,7 +39,7 @@ func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation neat // NewEvolvableSubstrateWithBias creates new instance of evolvable substrate with defined cppnBias value. // The cppnBias will be provided as first value of the CPPN inputs array. func NewEvolvableSubstrateWithBias(layout EvolvableSubstrateLayout, nodesActivation neatmath.NodeActivationType, cppnBias float64) *EvolvableSubstrate { - coords := make([]float64, 5) + coords := make([]float64, 7) coords[0] = cppnBias return &EvolvableSubstrate{ coords: coords, @@ -51,7 +51,7 @@ func NewEvolvableSubstrateWithBias(layout EvolvableSubstrateLayout, nodesActivat // CreateNetworkSolver Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which // used to define connections between network nodes. Optional graph_builder can be provided to collect graph nodes and edges // of created network solver. With graph builder it is possible to save/load network configuration as well as visualize it. -func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphBuilder SubstrateGraphBuilder, options *eshyperneat.Options) (network.Solver, error) { +func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuilder SubstrateGraphBuilder, options *eshyperneat.Options) (network.Solver, error) { es.cppn = cppn // the network layers will be collected in order: bias, input, output, hidden @@ -71,11 +71,11 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bo return nil, false } var link *network.FastNetworkLink - if useLeo && qp.CppnOut[1] > 0 { - link = createLink(qp.Weight(), source, target, options.WeightRange) - } else if !useLeo && math.Abs(qp.Weight()) > options.LinkThreshold { + if options.LeoEnabled && qp.Leo > 0 { + link = createLink(qp.Weight, source, target, options.WeightRange) + } else if !options.LeoEnabled && math.Abs(qp.Weight) >= options.LinkThreshold { // add only connections with signal exceeding provided threshold - link = createThresholdNormalizedLink(qp.Weight(), source, target, options.LinkThreshold, options.WeightRange) + link = createThresholdNormalizedLink(qp.Weight, source, target, options.LinkThreshold, options.WeightRange) } if link != nil { links = append(links, link) @@ -99,11 +99,11 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bo return nil, err } - if root, err = es.quadTreeDivideAndInit(input.X, input.Y, true, options); err != nil { + if root, err = es.quadTreeDivideAndInit(input.X, input.Y, input.Z, true, options); err != nil { return nil, err } qPoints := make([]*QuadPoint, 0) - if qPoints, err = es.pruneAndExpress(input.X, input.Y, qPoints, root, true, options); err != nil { + if qPoints, err = es.pruneAndExpress(input.X, input.Y, input.Z, qPoints, root, true, options); err != nil { return nil, err } // iterate over quad points and add nodes/links @@ -133,11 +133,11 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bo if err != nil { return nil, err } - if root, err = es.quadTreeDivideAndInit(hidden.X, hidden.Y, true, options); err != nil { + if root, err = es.quadTreeDivideAndInit(hidden.X, hidden.Y, hidden.Z, true, options); err != nil { return nil, err } qPoints := make([]*QuadPoint, 0) - if qPoints, err = es.pruneAndExpress(hidden.X, hidden.Y, qPoints, root, true, options); err != nil { + if qPoints, err = es.pruneAndExpress(hidden.X, hidden.Y, hidden.Z, qPoints, root, true, options); err != nil { return nil, err } // iterate over quad points and add nodes/links @@ -174,11 +174,11 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bo return nil, err } - if root, err = es.quadTreeDivideAndInit(output.X, output.Y, false, options); err != nil { + if root, err = es.quadTreeDivideAndInit(output.X, output.Y, output.Z, false, options); err != nil { return nil, err } qPoints := make([]*QuadPoint, 0) - if qPoints, err = es.pruneAndExpress(output.X, output.Y, qPoints, root, false, options); err != nil { + if qPoints, err = es.pruneAndExpress(output.X, output.Y, output.Z, qPoints, root, false, options); err != nil { return nil, err } @@ -217,9 +217,9 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, useLeo bo } // create fast network solver - if totalNeuronCount == 0 || (!useLeo && len(links) == 0) || len(activations) != totalNeuronCount { + if totalNeuronCount == 0 || len(activations) != totalNeuronCount { message := fmt.Sprintf("failed to create network solver: links [%d], nodes [%d], activations [%d], LEO [%t]", - len(links), totalNeuronCount, len(activations), useLeo) + len(links), totalNeuronCount, len(activations), options.LeoEnabled) return nil, errors.New(message) } solver := network.NewFastModularNetworkSolver( @@ -249,11 +249,11 @@ func (es *EvolvableSubstrate) addHiddenNode(qp *QuadPoint, firstHidden int, grap return targetIndex, nil } -// Divides and initialize the quadtree from provided coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b). -// Returns quadtree, in which each quadnode at (x,y) stores CPPN activation level for its position. The initialized +// Divides and initialize the quadtree from provided coordinates of source (outgoing = true) or target node (outgoing = false) at (a,b,c). +// Returns quadtree, in which each quadnode at (x,y,z) stores CPPN activation level for its position. The initialized // quadtree is used in the PruningAndExtraction phase to generate the actual ANN connections. -func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, options *eshyperneat.Options) (root *QuadNode, err error) { - root = NewQuadNode(0.0, 0.0, 1.0, 1) +func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b, c float64, outgoing bool, options *eshyperneat.Options) (root *QuadNode, err error) { + root = NewQuadNode(0.0, 0.0, options.Width, options.Height, 1) queue := list.New() queue.PushBack(root) @@ -264,21 +264,21 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, // Divide into subregions and assign children to parent p.Nodes = []*QuadNode{ - NewQuadNode(p.X-p.Width/2.0, p.Y-p.Width/2.0, p.Width/2.0, p.Level+1), - NewQuadNode(p.X-p.Width/2.0, p.Y+p.Width/2.0, p.Width/2.0, p.Level+1), - NewQuadNode(p.X+p.Width/2.0, p.Y-p.Width/2.0, p.Width/2.0, p.Level+1), - NewQuadNode(p.X+p.Width/2.0, p.Y+p.Width/2.0, p.Width/2.0, p.Level+1), + NewQuadNode(p.X-p.Width/2.0, p.Y-p.Height/2.0, p.Width/2.0, p.Height/2.0, p.Level+1), + NewQuadNode(p.X-p.Width/2.0, p.Y+p.Height/2.0, p.Width/2.0, p.Height/2.0, p.Level+1), + NewQuadNode(p.X+p.Width/2.0, p.Y-p.Height/2.0, p.Width/2.0, p.Height/2.0, p.Level+1), + NewQuadNode(p.X+p.Width/2.0, p.Y+p.Height/2.0, p.Width/2.0, p.Height/2.0, p.Level+1), } for _, node := range p.Nodes { if outgoing { // Querying connection from input or hidden node (Outgoing connectivity pattern) - if node.CppnOut, err = es.queryCPPN(a, b, node.X, node.Y); err != nil { + if node.CppnOut, err = es.queryCPPN(a, b, c, node.X, node.Y, node.Z); err != nil { return nil, err } } else { // Querying connection to output node (Incoming connectivity pattern) - if node.CppnOut, err = es.queryCPPN(node.X, node.Y, a, b); err != nil { + if node.CppnOut, err = es.queryCPPN(node.X, node.Y, node.Z, a, b, c); err != nil { return nil, err } } @@ -287,8 +287,8 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, // Divide until initial resolution or if variance is still high if p.Level < options.InitialDepth || (p.Level < options.MaximalDepth && nodeVariance(p) > options.DivisionThreshold) { - for _, c := range p.Nodes { - queue.PushBack(c) + for _, child := range p.Nodes { + queue.PushBack(child) } } } @@ -300,7 +300,7 @@ func (es *EvolvableSubstrate) quadTreeDivideAndInit(a, b float64, outgoing bool, // Receive coordinates of source (outgoing = true) or target node (outgoing = false) at (a, b) and initialized quadtree node. // Adds the connections that are in bands of the two-dimensional cross-section of the hypercube containing the source // or target node to the connections list and return modified list. -func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadPoint, node *QuadNode, outgoing bool, options *eshyperneat.Options) ([]*QuadPoint, error) { +func (es *EvolvableSubstrate) pruneAndExpress(a, b, c float64, connections []*QuadPoint, node *QuadNode, outgoing bool, options *eshyperneat.Options) ([]*QuadPoint, error) { // fast check if len(node.Nodes) == 0 { return connections, nil @@ -313,64 +313,66 @@ func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadP childVariance := nodeVariance(quadNode) if childVariance >= options.VarianceThreshold { - if conn, err := es.pruneAndExpress(a, b, connections, quadNode, outgoing, options); err != nil { + if conn, err := es.pruneAndExpress(a, b, c, connections, quadNode, outgoing, options); err != nil { return nil, err } else { connections = append(connections, conn...) } - } else { - // Determine if point is in a band by checking neighbor CPPN values + } else if !options.LeoEnabled || (quadNode.Leo() > 0) { + // Band Pruning phase. + // If LEO is turned off this should always happen. + // If it is not it should only happen if the LEO output is greater than zero if outgoing { - if l, err := es.queryCPPN(a, b, quadNode.X-node.Width, quadNode.Y); err != nil { + if l, err := es.queryCPPN(a, b, c, quadNode.X-node.Width, quadNode.Y, quadNode.Z); err != nil { return nil, err } else { left = math.Abs(quadNode.Weight() - l[0]) } - if r, err := es.queryCPPN(a, b, quadNode.X+node.Width, quadNode.Y); err != nil { + if r, err := es.queryCPPN(a, b, c, quadNode.X+node.Width, quadNode.Y, quadNode.Z); err != nil { return nil, err } else { right = math.Abs(quadNode.Weight() - r[0]) } - if t, err := es.queryCPPN(a, b, quadNode.X, quadNode.Y-node.Width); err != nil { + if t, err := es.queryCPPN(a, b, c, quadNode.X, quadNode.Y-node.Height, quadNode.Z); err != nil { return nil, err } else { top = math.Abs(quadNode.Weight() - t[0]) } - if b, err := es.queryCPPN(a, b, quadNode.X, quadNode.Y+node.Width); err != nil { + if bot, err := es.queryCPPN(a, b, c, quadNode.X, quadNode.Y+node.Height, quadNode.Z); err != nil { return nil, err } else { - bottom = math.Abs(quadNode.Weight() - b[0]) + bottom = math.Abs(quadNode.Weight() - bot[0]) } } else { - if l, err := es.queryCPPN(quadNode.X-node.Width, quadNode.Y, a, b); err != nil { + if l, err := es.queryCPPN(quadNode.X-node.Width, quadNode.Y, quadNode.Z, a, b, c); err != nil { return nil, err } else { left = math.Abs(quadNode.Weight() - l[0]) } - if r, err := es.queryCPPN(quadNode.X+node.Width, quadNode.Y, a, b); err != nil { + if r, err := es.queryCPPN(quadNode.X+node.Width, quadNode.Y, quadNode.Z, a, b, c); err != nil { return nil, err } else { right = math.Abs(quadNode.Weight() - r[0]) } - if t, err := es.queryCPPN(quadNode.X, quadNode.Y-node.Width, a, b); err != nil { + if t, err := es.queryCPPN(quadNode.X, quadNode.Y-node.Height, quadNode.Z, a, b, c); err != nil { return nil, err } else { top = math.Abs(quadNode.Weight() - t[0]) } - if b, err := es.queryCPPN(quadNode.X, quadNode.Y+node.Width, a, b); err != nil { + if bot, err := es.queryCPPN(quadNode.X, quadNode.Y+node.Height, quadNode.Z, a, b, c); err != nil { return nil, err } else { - bottom = math.Abs(quadNode.Weight() - b[0]) + bottom = math.Abs(quadNode.Weight() - bot[0]) } } if math.Max(math.Min(top, bottom), math.Min(left, right)) > options.BandingThreshold { - // Create new connection specified by QuadPoint(x1,y1,x2,y2,weight) in 4D hypercube + // Create new connection specified by QuadPoint(x1,y1,z1,x2,y2,z2,weight) in 4D hypercube var conn *QuadPoint if outgoing { - conn = NewQuadPoint(a, b, quadNode.X, quadNode.Y, quadNode) + conn = NewQuadPoint(a, b, c, quadNode.X, quadNode.Y, quadNode.Z, quadNode) } else { - conn = NewQuadPoint(quadNode.X, quadNode.Y, a, b, quadNode) + conn = NewQuadPoint(quadNode.X, quadNode.Y, quadNode.Z, a, b, c, quadNode) } connections = append(connections, conn) @@ -383,19 +385,21 @@ func (es *EvolvableSubstrate) pruneAndExpress(a, b float64, connections []*QuadP // Query CPPN associated with this substrate for specified Hypercube coordinate and returns value produced or error if // operation failed -func (es *EvolvableSubstrate) queryCPPN(x1, y1, x2, y2 float64) ([]float64, error) { +func (es *EvolvableSubstrate) queryCPPN(x1, y1, z1, x2, y2, z2 float64) ([]float64, error) { offset := 0 - if len(es.coords) == 5 { + if len(es.coords) == 7 { // CPPN bias defined offset = 1 } es.coords[offset] = x1 es.coords[offset+1] = y1 - es.coords[offset+2] = x2 - es.coords[offset+3] = y2 + es.coords[offset+2] = z1 + es.coords[offset+3] = x2 + es.coords[offset+4] = y2 + es.coords[offset+5] = z2 if outs, err := queryCPPN(es.coords, es.cppn); err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to query CPPN") } else { return outs, nil } diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index 4a461e1..9d95bb6 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -24,14 +24,14 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { // test solver creation graph := NewSubstrateGraphMLBuilder("TestEvolvableSubstrate_CreateNetworkSolver", false) - solver, err := substr.CreateNetworkSolver(cppn, false, graph, context) + solver, err := substr.CreateNetworkSolver(cppn, graph, context) require.NoError(t, err, "failed to create solver") printGraph(graph, t) totalNodeCount := inputCount + outputCount + layout.HiddenCount() assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong total node count") - assert.Equal(t, 8, solver.LinkCount(), "wrong link number") + assert.Equal(t, 30, solver.LinkCount(), "wrong link number") // check outputs outExpected := []float64{0.5, 0.5} @@ -49,17 +49,18 @@ func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { require.NoError(t, err, "failed to read CPPN") context, err := loadESHyperNeatOptions(esHyperNeatTestConfigFile) require.NoError(t, err, "failed to read ESHyperNEAT context") + context.LeoEnabled = true // test solver creation graph := NewSubstrateGraphMLBuilder("TestEvolvableSubstrate_CreateNetworkSolver", false) - solver, err := substr.CreateNetworkSolver(cppn, true, graph, context) + solver, err := substr.CreateNetworkSolver(cppn, graph, context) require.NoError(t, err, "failed to create solver") printGraph(graph, t) totalNodeCount := inputCount + outputCount + layout.HiddenCount() assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong total node count") - assert.Equal(t, 19, solver.LinkCount(), "wrong link number") + assert.Equal(t, 12, solver.LinkCount(), "wrong link number") // check outputs outExpected := []float64{0.5, 0.5} diff --git a/cppn/quad_tree.go b/cppn/quad_tree.go index 51b25b3..022de3e 100644 --- a/cppn/quad_tree.go +++ b/cppn/quad_tree.go @@ -15,7 +15,7 @@ func (p *PointF) String() string { return fmt.Sprintf("(%f, %f)", p.X, p.Y) } -// QuadPoint Defines the quad-point in the 4 dimensional hypercube +// QuadPoint Defines the quad-point in the 6 dimensional hypercube type QuadPoint struct { // The associated coordinates X1, X2, Y1, Y2, Z1, Z2 float64 @@ -26,7 +26,7 @@ type QuadPoint struct { } func (q *QuadPoint) String() string { - str := fmt.Sprintf("((%f, %f),(%f, %f)) = %f", q.X1, q.Y1, q.X2, q.Y2, q.Weight) + str := fmt.Sprintf("((%f, %f, %f),(%f, %f, %f)) = %f", q.X1, q.Y1, q.Z1, q.X2, q.Y2, q.Z2, q.Weight) if q.Leo >= 0 { var status string if q.Leo > 0 { @@ -40,8 +40,8 @@ func (q *QuadPoint) String() string { } // NewQuadPoint Creates new quad point -func NewQuadPoint(x1, y1, x2, y2 float64, node *QuadNode) *QuadPoint { - return &QuadPoint{X1: x1, Y1: y1, X2: x2, Y2: y2, Weight: node.Weight(), Leo: node.Leo()} +func NewQuadPoint(x1, y1, z1, x2, y2, z2 float64, node *QuadNode) *QuadPoint { + return &QuadPoint{X1: x1, Y1: y1, Z1: z1, X2: x2, Y2: y2, Z2: z2, Weight: node.Weight(), Leo: node.Leo()} } // QuadNode Defines quad-tree node to model 4 dimensional hypercube @@ -78,7 +78,7 @@ func (q *QuadNode) HasLeo() bool { } func (q *QuadNode) String() string { - return fmt.Sprintf("((%f, %f), %f x %f) = %f at %d", q.X, q.Y, q.Width, q.Height, q.CppnOut, q.Level) + return fmt.Sprintf("((%f, %f, %f), %f x %f) = %f at %d", q.X, q.Y, q.Z, q.Width, q.Height, q.CppnOut, q.Level) } // NewQuadNode Creates new quad-node with given parameters diff --git a/data/test/test_cppn_hyperneat_genome.yml b/data/test/test_cppn_hyperneat_genome.yml index 1d25a4f..def2398 100644 --- a/data/test/test_cppn_hyperneat_genome.yml +++ b/data/test/test_cppn_hyperneat_genome.yml @@ -19,17 +19,20 @@ genome: - {id: 3, trait_id: 0, type: INPT, activation: NullActivation} - {id: 4, trait_id: 0, type: INPT, activation: NullActivation} - {id: 5, trait_id: 0, type: INPT, activation: NullActivation} + - {id: 6, trait_id: 0, type: INPT, activation: NullActivation} + - {id: 7, trait_id: 0, type: INPT, activation: NullActivation} # The output nodes - actuators - - {id: 6, trait_id: 0, type: OUTP, activation: LinearActivation} + - {id: 8, trait_id: 0, type: OUTP, activation: TanhActivation} # The hidden node - - {id: 7, trait_id: 0, type: HIDN, activation: SineActivation} + - {id: 9, trait_id: 0, type: HIDN, activation: SineActivation} # The genes - connection between neuron nodes within this genome genes: - - {src_id: 1, tgt_id: 6, weight: 0.1, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 2, tgt_id: 6, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 3, tgt_id: 7, weight: 0.2, trait_id: 3, innov_num: 3, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 4, tgt_id: 7, weight: 0.3, trait_id: 4, innov_num: 4, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 5, tgt_id: 6, weight: 0.5, trait_id: 5, innov_num: 5, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 1, tgt_id: 7, weight: 0.4, trait_id: 6, innov_num: 6, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 7, tgt_id: 6, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} \ No newline at end of file + - {src_id: 1, tgt_id: 9, weight: 0.1, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 2, tgt_id: 9, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 3, tgt_id: 9, weight: 0.2, trait_id: 3, innov_num: 3, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 4, tgt_id: 9, weight: 0.3, trait_id: 4, innov_num: 4, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 5, tgt_id: 9, weight: 0.5, trait_id: 5, innov_num: 5, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 6, tgt_id: 9, weight: 0.4, trait_id: 6, innov_num: 6, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 7, tgt_id: 9, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 9, tgt_id: 8, weight: 0.8, trait_id: 7, innov_num: 8, mut_num: 0, recurrent: false, enabled: true} \ No newline at end of file diff --git a/data/test/test_cppn_leo_hyperneat_genome.yml b/data/test/test_cppn_leo_hyperneat_genome.yml index c347f26..ac3f3ea 100644 --- a/data/test/test_cppn_leo_hyperneat_genome.yml +++ b/data/test/test_cppn_leo_hyperneat_genome.yml @@ -17,27 +17,34 @@ genome: # The input nodes - sensors - {id: 2, trait_id: 0, type: INPT, activation: NullActivation} # x1 - {id: 3, trait_id: 0, type: INPT, activation: NullActivation} # y1 - - {id: 4, trait_id: 0, type: INPT, activation: NullActivation} # x2 - - {id: 5, trait_id: 0, type: INPT, activation: NullActivation} # y2 + - {id: 4, trait_id: 0, type: INPT, activation: NullActivation} # z1 + - {id: 5, trait_id: 0, type: INPT, activation: NullActivation} # x2 + - {id: 6, trait_id: 0, type: INPT, activation: NullActivation} # y2 + - {id: 7, trait_id: 0, type: INPT, activation: NullActivation} # z2 # The weight output - - {id: 6, trait_id: 0, type: OUTP, activation: TanhActivation} + - {id: 8, trait_id: 0, type: OUTP, activation: TanhActivation} # The LEO output - - {id: 7, trait_id: 0, type: OUTP, activation: StepActivation} + - {id: 9, trait_id: 0, type: OUTP, activation: StepActivation} # The hidden nodes - - {id: 8, trait_id: 0, type: HIDN, activation: SineActivation} - - {id: 9, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation} + - {id: 10, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation} + - {id: 11, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation} # The genes - connection between neuron nodes within this genome genes: - - {src_id: 1, tgt_id: 6, weight: 0.1, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 2, tgt_id: 6, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 3, tgt_id: 8, weight: 0.2, trait_id: 3, innov_num: 3, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 4, tgt_id: 8, weight: 0.3, trait_id: 4, innov_num: 4, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 5, tgt_id: 6, weight: 0.5, trait_id: 5, innov_num: 5, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 1, tgt_id: 8, weight: 0.4, trait_id: 6, innov_num: 6, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 8, tgt_id: 6, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 1, tgt_id: 10, weight: 0.1, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 1, tgt_id: 11, weight: 0.1, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 2, tgt_id: 10, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 3, tgt_id: 10, weight: 0.2, trait_id: 3, innov_num: 3, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 4, tgt_id: 10, weight: 0.3, trait_id: 4, innov_num: 4, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 5, tgt_id: 11, weight: 0.5, trait_id: 5, innov_num: 5, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 6, tgt_id: 11, weight: 0.4, trait_id: 6, innov_num: 6, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 7, tgt_id: 11, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} + + - {src_id: 10, tgt_id: 8, weight: 0.3, trait_id: 4, innov_num: 8, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 11, tgt_id: 8, weight: 0.3, trait_id: 4, innov_num: 9, mut_num: 0, recurrent: false, enabled: true} # The LEO part with local connectivity among X axis - - {src_id: 4, tgt_id: 9, weight: 0.3, trait_id: 4, innov_num: 4, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 1, tgt_id: 9, weight: 0.4, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 1, tgt_id: 7, weight: 0.3, trait_id: 2, innov_num: 2, mut_num: 0, recurrent: false, enabled: true} - - {src_id: 9, tgt_id: 7, weight: 0.8, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true} \ No newline at end of file + - {src_id: 1, tgt_id: 9, weight: 0.4, trait_id: 1, innov_num: 10, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 2, tgt_id: 9, weight: 0.4, trait_id: 1, innov_num: 11, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 5, tgt_id: 9, weight: 0.4, trait_id: 1, innov_num: 12, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 10, tgt_id: 9, weight: 0.3, trait_id: 2, innov_num: 13, mut_num: 0, recurrent: false, enabled: true} + - {src_id: 11, tgt_id: 9, weight: 0.8, trait_id: 7, innov_num: 14, mut_num: 0, recurrent: false, enabled: true} \ No newline at end of file diff --git a/data/test/test_es_hyper.neat.yml b/data/test/test_es_hyper.neat.yml index 07c26af..55511bb 100644 --- a/data/test/test_es_hyper.neat.yml +++ b/data/test/test_es_hyper.neat.yml @@ -2,7 +2,10 @@ # The HyperNEAT specific configuration # ######################################## # The threshold value to indicate which links should be included -link_threshold: 0.2 +link_threshold: 0.0 + +# Indicates whether Link Expression Output (LEO) enabled +leo_enabled: false # The weight range defines the minimum and maximum values for weights on substrate connections weight_range: 3 @@ -25,6 +28,11 @@ variance_threshold: 0.03 # BandingThreshold defines the threshold that determines when points are regarded to be in a band. banding_threshold: 0.3 +# Quadtree Dimensions +# The range of the tree. Typically set to 2.0 +width: 1.0 +height: 1.0 + # ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. # TODO BUG WHEN ES_ITERATIONS > 1 es_iterations: 1 From a9dad242394652cd8fe55bde0b9c94c9bab2e292 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 18 Apr 2024 18:57:03 +0300 Subject: [PATCH 43/57] Fixed substrate to use 3d coordinates. Fixed substrate tests --- cppn/substrate.go | 13 +++++++++++-- cppn/substrate_test.go | 16 ++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/cppn/substrate.go b/cppn/substrate.go index 729a218..e181fd4 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -65,7 +65,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } // give bias inputs to all hidden and output nodes. - coordinates := make([]float64, 4) + coordinates := make([]float64, 6) for bi := firstBias; bi < firstInput; bi++ { // the bias coordinates @@ -74,6 +74,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[0] = biasPosition.X coordinates[1] = biasPosition.Y + coordinates[2] = biasPosition.Z // add bias node to builder if _, err = addNodeToBuilder(graphBuilder, bi, network.BiasNeuron, activationForNeuron(bi), biasPosition); err != nil { @@ -89,6 +90,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[2] = hiddenPosition.X coordinates[3] = hiddenPosition.Y + coordinates[4] = hiddenPosition.Z // add node to graph if _, err = addNodeToBuilder(graphBuilder, hi, network.HiddenNeuron, activationForNeuron(hi), hiddenPosition); err != nil { @@ -115,6 +117,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[2] = outputPosition.X coordinates[3] = outputPosition.Y + coordinates[4] = outputPosition.Z // add node to graph if _, err = addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, activationForNeuron(oi), outputPosition); err != nil { @@ -143,6 +146,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[0] = inputPosition.X coordinates[1] = inputPosition.Y + coordinates[2] = inputPosition.Z // add node to graph if _, err = addNodeToBuilder(graphBuilder, in, network.InputNeuron, activationForNeuron(in), inputPosition); err != nil { @@ -156,6 +160,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[2] = hiddenPosition.X coordinates[3] = hiddenPosition.Y + coordinates[4] = hiddenPosition.Z } // find connection weight if link, err := fastNetworkLink(coordinates, cppn, useLeo, in, hi, options); err != nil { @@ -177,6 +182,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[0] = hiddenPosition.X coordinates[1] = hiddenPosition.Y + coordinates[2] = hiddenPosition.Z } for oi := firstOutput; oi < firstHidden; oi++ { // get output neuron coordinates @@ -185,6 +191,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[2] = outputPosition.X coordinates[3] = outputPosition.Y + coordinates[4] = outputPosition.Z } // find connection weight if link, err := fastNetworkLink(coordinates, cppn, useLeo, hi, oi, options); err != nil { @@ -207,6 +214,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[0] = inputPosition.X coordinates[1] = inputPosition.Y + coordinates[2] = inputPosition.Z // add node to graph if _, err = addNodeToBuilder(graphBuilder, in, network.InputNeuron, activationForNeuron(in), inputPosition); err != nil { @@ -220,6 +228,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB } else { coordinates[2] = outputPosition.X coordinates[3] = outputPosition.Y + coordinates[4] = outputPosition.Z } // find connection weight if link, err := fastNetworkLink(coordinates, cppn, useLeo, in, oi, options); err != nil { @@ -257,7 +266,7 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB func fastNetworkLink(coordinates []float64, cppn network.Solver, useLeo bool, source, target int, options *hyperneat.Options) (*network.FastNetworkLink, error) { if outs, err := queryCPPN(coordinates, cppn); err != nil { return nil, err - } else if useLeo && outs[1] >= options.LeoThreshold { + } else if useLeo && outs[1] > 0 { // add links only when CPPN LEO output signals to return createLink(outs[0], source, target, options.WeightRange), nil } else if !useLeo && math.Abs(outs[0]) >= options.LinkThreshold { diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index e25a33c..40552a9 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -44,11 +44,11 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { totalNodeCount := biasCount + inputCount + hiddenCount + outputCount assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong nodes number") - totalLinkCount := 12 //biasCount * (hiddenCount + outputCount) + totalLinkCount := 13 assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - outExpected := []float64{0.6427874813512032, 0.8685335941574246} + outExpected := []float64{0.6539661965642098, 0.6851172276942393} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } @@ -78,12 +78,12 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { totalNodeCount := biasCount + inputCount + hiddenCount + outputCount assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong nodes number") - totalLinkCount := 16 + totalLinkCount := 15 assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - outExpected := []float64{0.5, 0.5} - checkNetworkSolverOutputs(solver, outExpected, 1e-5, t) + outExpected := []float64{0.8409124463118298, 0.4783802890458948} + checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { @@ -113,7 +113,7 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { totalNodes := biasCount + inputCount + hiddenCount + outputCount assert.Len(t, graph.Nodes, totalNodes, "wrong nodes number") - totalEdges := 12 + totalEdges := 13 assert.Len(t, graph.Edges, totalEdges, "wrong edges number") var buf bytes.Buffer @@ -121,10 +121,10 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { require.NoError(t, err, "failed to marshal graph") strOut := buf.String() - assert.Equal(t, 5597, len(strOut), "wrong length of marshalled string") + assert.Equal(t, 5799, len(strOut), "wrong length of marshalled string") // test outputs - outExpected := []float64{0.6427874813512032, 0.8685335941574246} + outExpected := []float64{0.6539661965642098, 0.6851172276942393} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } From d36021adc0e53c2d475449b0e94404150001e6e2 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 18 Apr 2024 18:57:28 +0300 Subject: [PATCH 44/57] Fixed retina experiment and tests. --- data/retina/cppn_genome.yml | 31 +++++++++--------- data/retina/es_hyper.neat.yml | 17 +++++----- examples/retina/retina.go | 59 +++++++++++++++++++++------------- examples/retina/retina_test.go | 4 +-- executor.go | 3 +- hyperneat/hyper_neat.go | 4 +-- 6 files changed, 66 insertions(+), 52 deletions(-) diff --git a/data/retina/cppn_genome.yml b/data/retina/cppn_genome.yml index a948df1..bbfd1d1 100644 --- a/data/retina/cppn_genome.yml +++ b/data/retina/cppn_genome.yml @@ -18,24 +18,25 @@ genome: # The input nodes - sensors - { id: 2, trait_id: 0, type: INPT, activation: LinearActivation } # x1 - { id: 3, trait_id: 0, type: INPT, activation: LinearActivation } # y1 - - { id: 4, trait_id: 0, type: INPT, activation: LinearActivation } # x2 - - { id: 5, trait_id: 0, type: INPT, activation: LinearActivation } # y2 - # The output nodes - actuators - - { id: 6, trait_id: 0, type: OUTP, activation: LinearActivation } - - { id: 7, trait_id: 0, type: OUTP, activation: StepActivation } # LEO + - { id: 4, trait_id: 0, type: INPT, activation: LinearActivation } # z1 + - { id: 5, trait_id: 0, type: INPT, activation: LinearActivation } # x2 + - { id: 6, trait_id: 0, type: INPT, activation: LinearActivation } # y2 + - { id: 7, trait_id: 0, type: INPT, activation: LinearActivation } # z2 + # The output nodes - actuators TANH to make sure we have output values in range (-1, 1) + - { id: 8, trait_id: 0, type: OUTP, activation: TanhActivation } # The hidden node - - { id: 8, trait_id: 0, type: HIDN, activation: SigmoidSteepenedActivation } # G1 - - { id: 9, trait_id: 0, type: HIDN, activation: SigmoidSteepenedActivation } # G2 + - { id: 9, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G1 + - { id: 10, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G2 # The genes - connection between neuron nodes within this genome genes: - { src_id: 1, tgt_id: 9, weight: 0.5, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 1, tgt_id: 7, weight: 0.5, trait_id: 6, innov_num: 2, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 2, tgt_id: 8, weight: 0.5, trait_id: 2, innov_num: 3, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 1, tgt_id: 10, weight: 0.5, trait_id: 6, innov_num: 2, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 2, tgt_id: 9, weight: 0.5, trait_id: 2, innov_num: 3, mut_num: 0, recurrent: false, enabled: true } - { src_id: 3, tgt_id: 9, weight: 0.5, trait_id: 3, innov_num: 4, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 4, tgt_id: 8, weight: 0.5, trait_id: 4, innov_num: 5, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 5, tgt_id: 9, weight: 0.5, trait_id: 5, innov_num: 6, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 8, tgt_id: 6, weight: 0.5, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 8, tgt_id: 7, weight: 0.5, trait_id: 7, innov_num: 8, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 9, tgt_id: 6, weight: 0.5, trait_id: 8, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 9, tgt_id: 7, weight: 0.5, trait_id: 8, innov_num: 10, mut_num: 0, recurrent: false, enabled: true } \ No newline at end of file + - { src_id: 4, tgt_id: 9, weight: 0.5, trait_id: 4, innov_num: 5, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 5, tgt_id: 10, weight: 0.5, trait_id: 5, innov_num: 6, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 6, tgt_id: 10, weight: 0.5, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 7, tgt_id: 10, weight: 0.5, trait_id: 7, innov_num: 8, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 9, tgt_id: 8, weight: 0.5, trait_id: 8, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 10, tgt_id: 8, weight: 0.5, trait_id: 8, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } \ No newline at end of file diff --git a/data/retina/es_hyper.neat.yml b/data/retina/es_hyper.neat.yml index 8cf7c29..d92346b 100644 --- a/data/retina/es_hyper.neat.yml +++ b/data/retina/es_hyper.neat.yml @@ -101,9 +101,10 @@ node_activators: # The HyperNEAT specific configuration # ######################################## # The threshold value to indicate which links should be included -link_threshold: 0.1 -# Threshold above which a connection is expressed if Link Expression Output (LEO) enabled -leo_threshold: 0.5 +link_threshold: 0.0 + +# Indicates whether Link Expression Output (LEO) enabled +leo_enabled: false # The weight range defines the minimum and maximum values for weights on substrate connections weight_range: 1 @@ -118,21 +119,21 @@ cppn_bias: 0.33 # The ES-HyperNEAT specific configuration # ########################################### # InitialDepth defines the initial ES-HyperNEAT sample resolution. -initial_depth: 1 +initial_depth: 2 # Maximal ES-HyperNEAT sample resolution if the variance is still higher than the given division threshold maximal_depth: 3 # DivisionThreshold defines the substrate division threshold. division_threshold: 0.5 # VarianceThreshold defines the variance threshold for the initial sampling. -variance_threshold: 0.1 +variance_threshold: 0.03 # BandingThreshold defines the threshold that determines when points are regarded to be in a band. -banding_threshold: 0.5 +banding_threshold: 0.3 # Quadtree Dimensions # The range of the tree. Typically set to 2.0 -width: 2.0 -height: 2.0 +width: 1.0 +height: 1.0 # ESIterations defines how many times ES-HyperNEAT should iteratively discover new hidden nodes. es_iterations: 1 diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 3b4b272..4d51e58 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -15,15 +15,16 @@ import ( "github.com/yaricom/goNEAT/v4/neat/network" "math" "os" + "time" ) const ( // maxFitness Used as max value which we add error too to get an organism's fitness - maxFitness = 1000.0 + maxFitness = 1.0 // fitnessThreshold is the fitness value for which an organism is considered to have won the experiment fitnessThreshold = maxFitness - debug = false + debug = true ) type generationEvaluator struct { @@ -34,20 +35,17 @@ type generationEvaluator struct { numSpeciesTarget int // The species compatibility threshold adjustment frequency compatAdjustFreq int - // The flag to indicate if Link Expression Output should be enabled in CPPN - useLeo bool } // NewGenerationEvaluator is to create new generation's evaluator for retina experiment. The numSpeciesTarget specifies the // target number of species to maintain in the population. If the number of species differ from the numSpeciesTarget it // will be automatically adjusted with compatAdjustFreq frequency, i.e., at each epoch % compatAdjustFreq == 0 -func NewGenerationEvaluator(outDir string, env *Environment, numSpeciesTarget, compatAdjustFreq int, useLeo bool) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { +func NewGenerationEvaluator(outDir string, env *Environment, numSpeciesTarget, compatAdjustFreq int) (experiment.GenerationEvaluator, experiment.TrialRunObserver) { evaluator := &generationEvaluator{ outDir: outDir, env: env, numSpeciesTarget: numSpeciesTarget, compatAdjustFreq: compatAdjustFreq, - useLeo: useLeo, } return evaluator, evaluator } @@ -81,6 +79,7 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population ) var bestSubstrateSolver network.Solver + startTime := time.Now() for _, organism := range population.Organisms { isWinner, solver, err := e.organismEvaluate(ctx, organism) if err != nil { @@ -115,6 +114,7 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population } } } + elapsedTime := time.Now().Sub(startTime) // Fill statistics about current epoch epoch.FillPopulationStatistics(population) @@ -166,9 +166,9 @@ func (e *generationEvaluator) GenerationEvaluate(ctx context.Context, population } neat.InfoLog( - fmt.Sprintf("%d species -> %d organisms [compatibility threshold: %.1f, target: %d]\nbest CPNN organism [fitness: %.2f, links: %d, nodes: %d], best solver [links: %d, nodes: %d]", + fmt.Sprintf("%d species -> %d organisms [compatibility threshold: %.1f, target: %d]\nbest CPNN organism [fitness: %.2f, links: %d, nodes: %d], best solver [links: %d, nodes: %d], population evaluation time: %v", speciesCount, len(population.Organisms), options.CompatThreshold, e.numSpeciesTarget, - maxPopulationFitness, bestLinkCount, bestNodeCount, bestSolverLinks, bestSolverNodes)) + maxPopulationFitness, bestLinkCount, bestNodeCount, bestSolverLinks, bestSolverNodes, elapsedTime)) } return nil } @@ -180,13 +180,9 @@ func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *ge return false, nil, eshyperneat.ErrESHyperNEATOptionsNotFound } // get CPPN network solver - //cppnSolver, err := organism.Phenotype.FastNetworkSolver() - //if err != nil { - // return false, err - //} cppnSolver, err := organism.Phenotype() if err != nil { - return false, nil, err + return false, nil, errors.Wrap(err, "failed to create CPPN solver") } // create substrate layout @@ -198,13 +194,16 @@ func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *ge // create ES-HyperNEAT solver substr := cppn.NewEvolvableSubstrateWithBias(layout, options.SubstrateActivator.SubstrateActivationType, options.CppnBias) graph := cppn.NewSubstrateGraphMLBuilder("retina ES-HyperNEAT", false) - solver, err := substr.CreateNetworkSolver(cppnSolver, e.useLeo, graph, options) + createSolverTime := time.Now() + solver, err := substr.CreateNetworkSolver(cppnSolver, graph, options) if err != nil { return false, nil, errors.Wrap(err, fmt.Sprintf("failed to evaluate organism: %s", organism)) } + createSolverElapsedTime := time.Now().Sub(createSolverTime) // Evaluate the detector ANN against 256 combinations of the left and the right visual objects // at correct and incorrect sides of retina + startTime := time.Now() errorSum, count, detectionErrorCount := 0.0, 0.0, 0.0 for _, leftObj := range e.env.visualObjects { for _, rightObj := range e.env.visualObjects { @@ -226,6 +225,7 @@ func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *ge } } } + elapsed := time.Since(startTime) // Calculate the fitness score fitness := maxFitness / (1.0 + errorSum) @@ -246,8 +246,9 @@ func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *ge if debug { neat.InfoLog(fmt.Sprintf("Average error: %f, errors sum: %f, false detections: %f from: %f", avgError, errorSum, detectionErrorCount, count)) - neat.InfoLog(fmt.Sprintf("Substrate: #nodes = %d, #edges = %d | CPPN phenotype: #nodes = %d, #edges = %d", + neat.InfoLog(fmt.Sprintf("Substrate: nodes = %d, edges = %d | CPPN phenotype: nodes = %d, edges = %d", solver.NodeCount(), solver.LinkCount(), cppnSolver.NodeCount(), cppnSolver.LinkCount())) + neat.InfoLog(fmt.Sprintf("Substrate: evaluation time = %v, create solver time = %v", elapsed, createSolverElapsedTime)) } return isWinner, solver, nil @@ -312,17 +313,29 @@ func evaluatePredictions(predictions []float64, leftObj VisualObject, rightObj V targets[1] = 0.0 } - // Find loss as a Euclidean distance between outputs and ground truth - loss := (normPredictions[0]-targets[0])*(normPredictions[0]-targets[0]) + (normPredictions[1]-targets[1])*(normPredictions[1]-targets[1]) - - flag := "match" - if loss != 0 { - flag = "-" + // Find normalized loss + normLoss := (normPredictions[0]-targets[0])*(normPredictions[0]-targets[0]) + (normPredictions[1]-targets[1])*(normPredictions[1]-targets[1]) + if normLoss == 0 { + return 0.0 } - neat.DebugLog(fmt.Sprintf("[%.2f, %.2f] -> [%.2f, %.2f] '%s'", - targets[0], targets[1], normPredictions[0], normPredictions[1], flag)) + // find loss + loss := histDiff(predictions, targets) + + neat.DebugLog(fmt.Sprintf("[%.2f, %.2f] -> [%.2f, %.2f] loss: %.2f", + targets[0], targets[1], normPredictions[0], normPredictions[1], normLoss)) return loss } + +// calculates item-wise difference between two vectors +func histDiff(left, right []float64) float64 { + size := len(left) + diffAccum := 0.0 + for i := 0; i < size; i++ { + diff := left[i] - right[i] + diffAccum += math.Abs(diff) + } + return diffAccum / float64(size) +} diff --git a/examples/retina/retina_test.go b/examples/retina/retina_test.go index 756bd1b..66d1a19 100644 --- a/examples/retina/retina_test.go +++ b/examples/retina/retina_test.go @@ -10,8 +10,8 @@ func Test_evaluatePredictions(t *testing.T) { sumLoss := 0.0 for _, leftObj := range dataset { for _, rightObj := range dataset { - sumLoss += evaluatePredictions([]float64{0, 0, 0, 0}, leftObj, rightObj) + sumLoss += evaluatePredictions([]float64{0, 0}, leftObj, rightObj) } } - assert.Equal(t, 416.0, sumLoss) + assert.Equal(t, 208.0, sumLoss) } diff --git a/executor.go b/executor.go index 3ebefa3..638c924 100644 --- a/executor.go +++ b/executor.go @@ -25,7 +25,6 @@ func main() { var experimentName = flag.String("experiment", "retina", "The name of experiment to run. [retina]") var speciesTarget = flag.Int("species_target", 15, "The target number of species to maintain.") var speciesCompatAdjustFreq = flag.Int("species_adjust_freq", 10, "The frequency of species compatibility threshold adjustments when trying to maintain their number.") - var useLeo = flag.Bool("use_leo", false, "The flag to indicate if Link Expression Output should be enabled in CPPN") var logLevel = flag.String("log-level", "", "The logger level to be used. Overrides the one set in configuration.") var trialsCount = flag.Int("trials", 0, "The number of trials for experiment. Overrides the one set in configuration.") @@ -106,7 +105,7 @@ func main() { log.Fatalf("Failed to create retina environment, reason: %s", err) } else { generationEvaluator, trialObserver = retina.NewGenerationEvaluator( - *outDirPath, env, *speciesTarget, *speciesCompatAdjustFreq, *useLeo) + *outDirPath, env, *speciesTarget, *speciesCompatAdjustFreq) } default: log.Fatalf("Unsupported experiment name requested: %s\n", *experimentName) diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index 46c1f61..bdaf910 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -22,8 +22,8 @@ type Options struct { // from -WeightRange to +WeightRange, and can be any integer WeightRange float64 `yaml:"weight_range"` - // LeoThreshold Threshold above which a connection is expressed if Link Expression Output (LEO) enabled - LeoThreshold float64 `yaml:"leo_threshold"` + // LeoEnabled flag to control if Link Expression Output (LEO) enabled + LeoEnabled bool `yaml:"leo_enabled"` // The substrate activation function SubstrateActivator SubstrateActivatorType `yaml:"substrate_activator"` From 59b5d54d4cea60e9c35750cdec8dcb3d8d0d8159 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 18 Apr 2024 20:59:50 +0300 Subject: [PATCH 45/57] Implemented support for different activation types for hidden and output nodes of evolvable substrate. --- cppn/evolvable_substrate.go | 42 ++++++++++++++++++-------------- cppn/evolvable_substrate_test.go | 12 ++++----- data/retina/es_hyper.neat.yml | 6 +++-- examples/retina/retina.go | 9 ++++--- hyperneat/hyper_neat.go | 24 +++++++++++++++--- 5 files changed, 59 insertions(+), 34 deletions(-) diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index a3061d8..1b3b22b 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -16,10 +16,12 @@ import ( // by human, but produced during substrate generation from controlling CPPN and nodes locations may be arbitrary that suits // the best for the task at hand. type EvolvableSubstrate struct { - // The layout of neuron nodes in this substrate + // Layout The layout of neuron nodes in this substrate Layout EvolvableSubstrateLayout - // The activation function's type for neurons encoded - NodesActivation neatmath.NodeActivationType + // HiddenNodesActivation The activation function type for hidden neurons encoded + HiddenNodesActivation neatmath.NodeActivationType + // OutputNodesActivation The activation function type for output neurons encoded + OutputNodesActivation neatmath.NodeActivationType // The CPPN network solver to describe geometry of substrate cppn network.Solver @@ -28,23 +30,25 @@ type EvolvableSubstrate struct { } // NewEvolvableSubstrate Creates new instance of evolvable substrate -func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, nodesActivation neatmath.NodeActivationType) *EvolvableSubstrate { +func NewEvolvableSubstrate(layout EvolvableSubstrateLayout, hiddenNodesActivation, outputNodesActivation neatmath.NodeActivationType) *EvolvableSubstrate { return &EvolvableSubstrate{ - coords: make([]float64, 6), - Layout: layout, - NodesActivation: nodesActivation, + coords: make([]float64, 6), + Layout: layout, + HiddenNodesActivation: hiddenNodesActivation, + OutputNodesActivation: outputNodesActivation, } } // NewEvolvableSubstrateWithBias creates new instance of evolvable substrate with defined cppnBias value. // The cppnBias will be provided as first value of the CPPN inputs array. -func NewEvolvableSubstrateWithBias(layout EvolvableSubstrateLayout, nodesActivation neatmath.NodeActivationType, cppnBias float64) *EvolvableSubstrate { +func NewEvolvableSubstrateWithBias(layout EvolvableSubstrateLayout, hiddenNodesActivation, outputNodesActivation neatmath.NodeActivationType, cppnBias float64) *EvolvableSubstrate { coords := make([]float64, 7) coords[0] = cppnBias return &EvolvableSubstrate{ - coords: coords, - Layout: layout, - NodesActivation: nodesActivation, + coords: coords, + Layout: layout, + HiddenNodesActivation: hiddenNodesActivation, + OutputNodesActivation: outputNodesActivation, } } @@ -170,7 +174,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } // add output node to graph - if _, err = addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, es.NodesActivation, output); err != nil { + if _, err = addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, es.HiddenNodesActivation, output); err != nil { return nil, err } @@ -207,13 +211,15 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil activations := make([]neatmath.NodeActivationType, totalNeuronCount) for i := 0; i < totalNeuronCount; i++ { if i < firstOutput { - // input nodes - NULL activation - activations[i] = neatmath.NullActivation + // input nodes + activations[i] = neatmath.LinearActivation + } else if i < firstHidden { + // output nodes activations + activations[i] = es.OutputNodesActivation } else { - // hidden/output nodes - defined activation - activations[i] = es.NodesActivation + // hidden nodes activation + activations[i] = es.HiddenNodesActivation } - } // create fast network solver @@ -239,7 +245,7 @@ func (es *EvolvableSubstrate) addHiddenNode(qp *QuadPoint, firstHidden int, grap targetIndex += firstHidden // adjust index to the global indexes space // add a node to the graph - if _, err = addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.NodesActivation, nodePoint); err != nil { + if _, err = addNodeToBuilder(graphBuilder, targetIndex, network.HiddenNeuron, es.HiddenNodesActivation, nodePoint); err != nil { return -1, err } } else { diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index 9d95bb6..7649dac 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -15,7 +15,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { layout, err := NewMappedEvolvableSubstrateLayout(inputCount, outputCount) require.NoError(t, err, "failed to create layout") - substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation) + substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") @@ -34,8 +34,8 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { assert.Equal(t, 30, solver.LinkCount(), "wrong link number") // check outputs - outExpected := []float64{0.5, 0.5} - checkNetworkSolverOutputs(solver, outExpected, 1e-8, t) + outExpected := []float64{0, 0} + checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { @@ -43,7 +43,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { layout, err := NewMappedEvolvableSubstrateLayout(inputCount, outputCount) require.NoError(t, err, "failed to create layout") - substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation) + substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) cppn, err := FastSolverFromGenomeFile(cppnLeoHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") @@ -63,8 +63,8 @@ func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { assert.Equal(t, 12, solver.LinkCount(), "wrong link number") // check outputs - outExpected := []float64{0.5, 0.5} - checkNetworkSolverOutputs(solver, outExpected, 1e-8, t) + outExpected := []float64{0, 0} + checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } // Loads ES-HyperNeat options from provided config file's path diff --git a/data/retina/es_hyper.neat.yml b/data/retina/es_hyper.neat.yml index d92346b..6db9b17 100644 --- a/data/retina/es_hyper.neat.yml +++ b/data/retina/es_hyper.neat.yml @@ -109,8 +109,10 @@ leo_enabled: false # The weight range defines the minimum and maximum values for weights on substrate connections weight_range: 1 -# The substrate activation function, determines which activation function each node in the substrate will have. -substrate_activator: SigmoidPlainActivation +# The activation function for hidden substrate nodes. +substrate_activator: SigmoidBipolarActivation +# The activation function for output substrate nodes. +output_activator: SigmoidPlainActivation # The BIAS value of the CPPN network if appropriate [default: 1.0] cppn_bias: 0.33 diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 4d51e58..e91e429 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -20,7 +20,7 @@ import ( const ( // maxFitness Used as max value which we add error too to get an organism's fitness - maxFitness = 1.0 + maxFitness = 256.0 // fitnessThreshold is the fitness value for which an organism is considered to have won the experiment fitnessThreshold = maxFitness @@ -192,7 +192,8 @@ func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *ge return false, nil, err } // create ES-HyperNEAT solver - substr := cppn.NewEvolvableSubstrateWithBias(layout, options.SubstrateActivator.SubstrateActivationType, options.CppnBias) + substr := cppn.NewEvolvableSubstrateWithBias( + layout, options.SubstrateActivator.SubstrateActivationType, options.OutputActivator.OutputActivationType, options.CppnBias) graph := cppn.NewSubstrateGraphMLBuilder("retina ES-HyperNEAT", false) createSolverTime := time.Now() solver, err := substr.CreateNetworkSolver(cppnSolver, graph, options) @@ -244,8 +245,8 @@ func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *ge organism.Fitness = fitness if debug { - neat.InfoLog(fmt.Sprintf("Average error: %f, errors sum: %f, false detections: %f from: %f", - avgError, errorSum, detectionErrorCount, count)) + neat.InfoLog(fmt.Sprintf("Average error: %f, errors sum: %f, fitness: %f, false detections: %.0f from: %.0f", + avgError, errorSum, fitness, detectionErrorCount, count)) neat.InfoLog(fmt.Sprintf("Substrate: nodes = %d, edges = %d | CPPN phenotype: nodes = %d, edges = %d", solver.NodeCount(), solver.LinkCount(), cppnSolver.NodeCount(), cppnSolver.LinkCount())) neat.InfoLog(fmt.Sprintf("Substrate: evaluation time = %v, create solver time = %v", elapsed, createSolverElapsedTime)) diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index bdaf910..db05020 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -14,20 +14,27 @@ type SubstrateActivatorType struct { SubstrateActivationType math.NodeActivationType } +type OutputActivatorType struct { + OutputActivationType math.NodeActivationType +} + // Options The HyperNEAT execution options type Options struct { - // The threshold value to indicate which links should be included + // LinkThreshold The threshold value to indicate which links should be included LinkThreshold float64 `yaml:"link_threshold"` - // The weight range defines the minimum and maximum values for weights on substrate connections, they go + // WeightRange The weight range defines the minimum and maximum values for weights on substrate connections, they go // from -WeightRange to +WeightRange, and can be any integer WeightRange float64 `yaml:"weight_range"` // LeoEnabled flag to control if Link Expression Output (LEO) enabled LeoEnabled bool `yaml:"leo_enabled"` - // The substrate activation function + // SubstrateActivator The activation function for the hidden substrate nodes SubstrateActivator SubstrateActivatorType `yaml:"substrate_activator"` - // The BIAS value for CPPN network + // OutputActivatorType The activation function for the output substrate nodes + OutputActivator OutputActivatorType `yaml:"output_activator"` + + // CppnBias The BIAS value for CPPN network CppnBias float64 `yaml:"cppn_bias,omitempty"` } @@ -62,3 +69,12 @@ func (s *SubstrateActivatorType) UnmarshalYAML(value *yaml.Node) error { } return nil } + +func (o *OutputActivatorType) UnmarshalYAML(value *yaml.Node) error { + if activationType, err := math.NodeActivators.ActivationTypeFromName(value.Value); err != nil { + return errors.Wrap(err, "failed to decode output activator function from HyperNEAT options") + } else { + o.OutputActivationType = activationType + } + return nil +} From 218fcb4acf32375cbffc46c4aa7701162496fd4a Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 18 Apr 2024 21:16:18 +0300 Subject: [PATCH 46/57] Implemented support for different activation types for hidden and output nodes of HyperNEAT substrate. Fixed related test cases. --- cppn/evolvable_substrate.go | 29 +++++++++++++++++------------ cppn/quad_tree.go | 2 +- cppn/substrate.go | 26 ++++++++++++++++---------- cppn/substrate_test.go | 22 +++++++++++----------- data/test/test_es_hyper.neat.yml | 2 ++ data/test/test_hyper.neat.yml | 4 +++- eshyperneat/es_hyper_neat_test.go | 1 + hyperneat/hyper_neat_test.go | 1 + 8 files changed, 52 insertions(+), 35 deletions(-) diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index 1b3b22b..b65567f 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -90,6 +90,20 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil } } + // inline function to find activation type for a given neuron + activationForNeuron := func(nodeIndex int) neatmath.NodeActivationType { + if nodeIndex < firstOutput { + // input nodes + return neatmath.LinearActivation + } else if nodeIndex < firstHidden { + // output nodes activations + return es.OutputNodesActivation + } else { + // hidden nodes activation + return es.HiddenNodesActivation + } + } + // Build links from input nodes to the hidden nodes var root *QuadNode for in := firstInput; in < firstOutput; in++ { @@ -99,7 +113,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } // add input node to graph - if _, err = addNodeToBuilder(graphBuilder, in, network.InputNeuron, neatmath.NullActivation, input); err != nil { + if _, err = addNodeToBuilder(graphBuilder, in, network.InputNeuron, activationForNeuron(in), input); err != nil { return nil, err } @@ -174,7 +188,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil return nil, err } // add output node to graph - if _, err = addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, es.HiddenNodesActivation, output); err != nil { + if _, err = addNodeToBuilder(graphBuilder, oi, network.OutputNeuron, activationForNeuron(oi), output); err != nil { return nil, err } @@ -210,16 +224,7 @@ func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuil // build activations activations := make([]neatmath.NodeActivationType, totalNeuronCount) for i := 0; i < totalNeuronCount; i++ { - if i < firstOutput { - // input nodes - activations[i] = neatmath.LinearActivation - } else if i < firstHidden { - // output nodes activations - activations[i] = es.OutputNodesActivation - } else { - // hidden nodes activation - activations[i] = es.HiddenNodesActivation - } + activations[i] = activationForNeuron(i) } // create fast network solver diff --git a/cppn/quad_tree.go b/cppn/quad_tree.go index 022de3e..2d6353c 100644 --- a/cppn/quad_tree.go +++ b/cppn/quad_tree.go @@ -12,7 +12,7 @@ func NewPointF(x, y float64) *PointF { } func (p *PointF) String() string { - return fmt.Sprintf("(%f, %f)", p.X, p.Y) + return fmt.Sprintf("(%f, %f, %f)", p.X, p.Y, p.Z) } // QuadPoint Defines the quad-point in the 6 dimensional hypercube diff --git a/cppn/substrate.go b/cppn/substrate.go index e181fd4..6a7841f 100644 --- a/cppn/substrate.go +++ b/cppn/substrate.go @@ -13,18 +13,21 @@ import ( // the ANN neurons are encoded as coordinates in hypercube presented by this substrate. // By default, neurons will be placed into substrate within grid layout type Substrate struct { - // The layout of neuron nodes in this substrate + // Layout The layout of neuron nodes in this substrate Layout SubstrateLayout - // The activation function's type for neurons encoded - NodesActivation neatmath.NodeActivationType + // HiddenNodesActivation The activation function's type for neurons encoded + HiddenNodesActivation neatmath.NodeActivationType + // OutputNodesActivation The activation function type for output neurons encoded + OutputNodesActivation neatmath.NodeActivationType } // NewSubstrate creates new instance of substrate. -func NewSubstrate(layout SubstrateLayout, nodesActivation neatmath.NodeActivationType) *Substrate { +func NewSubstrate(layout SubstrateLayout, hiddenNodesActivation, outputNodesActivation neatmath.NodeActivationType) *Substrate { substr := Substrate{ - Layout: layout, - NodesActivation: nodesActivation, + Layout: layout, + HiddenNodesActivation: hiddenNodesActivation, + OutputNodesActivation: outputNodesActivation, } return &substr } @@ -56,11 +59,14 @@ func (s *Substrate) CreateNetworkSolver(cppn network.Solver, useLeo bool, graphB // inline function to find activation type for a given neuron activationForNeuron := func(nodeIndex int) neatmath.NodeActivationType { if nodeIndex < firstOutput { - // all bias and input neurons has null activation function associated because they actually have - // no inputs to be activated upon - return neatmath.NullActivation + // input nodes + return neatmath.LinearActivation + } else if nodeIndex < firstHidden { + // output nodes activations + return s.OutputNodesActivation } else { - return s.NodesActivation + // hidden nodes activation + return s.HiddenNodesActivation } } diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 40552a9..8e6215f 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -16,8 +16,8 @@ func TestNewSubstrate(t *testing.T) { layout := NewGridSubstrateLayout(biasCount, inputCount, outputCount, hiddenCount) // create new substrate - substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) - assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) + assert.Equal(t, math.SigmoidSteepenedActivation, substr.HiddenNodesActivation) } func TestSubstrate_CreateNetworkSolver(t *testing.T) { @@ -25,8 +25,8 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { layout := NewGridSubstrateLayout(biasCount, inputCount, outputCount, hiddenCount) // create new substrate - substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) - assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) + assert.Equal(t, math.SigmoidSteepenedActivation, substr.HiddenNodesActivation) // create solver from substrate cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) @@ -48,7 +48,7 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - outExpected := []float64{0.6539661965642098, 0.6851172276942393} + outExpected := []float64{0.12926155695140656, 0.15786889573150728} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } @@ -57,8 +57,8 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { layout := NewGridSubstrateLayout(biasCount, inputCount, outputCount, hiddenCount) // create new substrate - substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) - assert.Equal(t, math.SigmoidSteepenedActivation, substr.NodesActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) + assert.Equal(t, math.SigmoidSteepenedActivation, substr.HiddenNodesActivation) // create solver from substrate cppn, err := FastSolverFromGenomeFile(cppnLeoHyperNEATTestGenomePath) @@ -82,7 +82,7 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - outExpected := []float64{0.8409124463118298, 0.4783802890458948} + outExpected := []float64{0.33812764749536894, -0.017572705522999554} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } @@ -94,7 +94,7 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { builder := NewSubstrateGraphMLBuilder("", false).(*graphMLBuilder) // create new substrate - substr := NewSubstrate(layout, math.SigmoidSteepenedActivation) + substr := NewSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) // create solver from substrate cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) @@ -121,10 +121,10 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { require.NoError(t, err, "failed to marshal graph") strOut := buf.String() - assert.Equal(t, 5799, len(strOut), "wrong length of marshalled string") + assert.Equal(t, 5789, len(strOut), "wrong length of marshalled string") // test outputs - outExpected := []float64{0.6539661965642098, 0.6851172276942393} + outExpected := []float64{0.12926155695140656, 0.15786889573150728} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } diff --git a/data/test/test_es_hyper.neat.yml b/data/test/test_es_hyper.neat.yml index 55511bb..7356713 100644 --- a/data/test/test_es_hyper.neat.yml +++ b/data/test/test_es_hyper.neat.yml @@ -12,6 +12,8 @@ weight_range: 3 # The substrate activation function, determines which activation function each node in the substrate will have. substrate_activator: SigmoidSteepenedActivation +# The activation function for output substrate nodes. +output_activator: SigmoidPlainActivation ########################################### # The ES-HyperNEAT specific configuration # diff --git a/data/test/test_hyper.neat.yml b/data/test/test_hyper.neat.yml index 43335ee..91388c9 100644 --- a/data/test/test_hyper.neat.yml +++ b/data/test/test_hyper.neat.yml @@ -8,4 +8,6 @@ link_threshold: 0.2 weight_range: 3 # The substrate activation function, determines which activation function each node in the substrate will have. -substrate_activator: SigmoidSteepenedActivation \ No newline at end of file +substrate_activator: SigmoidSteepenedActivation +# The activation function for output substrate nodes. +output_activator: SigmoidPlainActivation \ No newline at end of file diff --git a/eshyperneat/es_hyper_neat_test.go b/eshyperneat/es_hyper_neat_test.go index dc3a694..ca10fa7 100644 --- a/eshyperneat/es_hyper_neat_test.go +++ b/eshyperneat/es_hyper_neat_test.go @@ -38,4 +38,5 @@ func checkEsHyperNeatOptions(opts *Options, t *testing.T) { assert.Equal(t, 1, opts.ESIterations) assert.Equal(t, math.SigmoidSteepenedActivation, opts.SubstrateActivator.SubstrateActivationType) + assert.Equal(t, math.SigmoidPlainActivation, opts.OutputActivator.OutputActivationType) } diff --git a/hyperneat/hyper_neat_test.go b/hyperneat/hyper_neat_test.go index 5060dce..b25ee86 100644 --- a/hyperneat/hyper_neat_test.go +++ b/hyperneat/hyper_neat_test.go @@ -31,6 +31,7 @@ func TestLoadYAMLConfigFile(t *testing.T) { func checkHyperNeatOptions(opts *Options, t *testing.T) { assert.Equal(t, math.SigmoidSteepenedActivation, opts.SubstrateActivator.SubstrateActivationType) + assert.Equal(t, math.SigmoidPlainActivation, opts.OutputActivator.OutputActivationType) assert.Equal(t, 0.2, opts.LinkThreshold) assert.Equal(t, 3.0, opts.WeightRange) } From a51a7efd4de0614c54c43b02f7fbc8733e4c640e Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Fri, 19 Apr 2024 17:59:20 +0300 Subject: [PATCH 47/57] Optimized loss calculation by flattening loop --- examples/retina/retina.go | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/examples/retina/retina.go b/examples/retina/retina.go index e91e429..bac27a1 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -320,23 +320,11 @@ func evaluatePredictions(predictions []float64, leftObj VisualObject, rightObj V return 0.0 } - // find loss - loss := histDiff(predictions, targets) + // find loss as item-wise difference between two vectors + loss := (math.Abs(predictions[0]-targets[0]) + math.Abs(predictions[1]-targets[1])) / 2.0 neat.DebugLog(fmt.Sprintf("[%.2f, %.2f] -> [%.2f, %.2f] loss: %.2f", targets[0], targets[1], normPredictions[0], normPredictions[1], normLoss)) return loss - -} - -// calculates item-wise difference between two vectors -func histDiff(left, right []float64) float64 { - size := len(left) - diffAccum := 0.0 - for i := 0; i < size; i++ { - diff := left[i] - right[i] - diffAccum += math.Abs(diff) - } - return diffAccum / float64(size) } From ecd8f6aab629d4fd1d575a379abf089d07f341e2 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 16 May 2024 19:31:43 +0300 Subject: [PATCH 48/57] Updated dependencies. Fixed deprecations --- go.mod | 8 ++++---- go.sum | 20 ++++++++++---------- hyperneat/hyper_neat.go | 3 +-- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index cf2c0a4..4b735c6 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,9 @@ go 1.21 require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 - github.com/yaricom/goGraphML v1.4.1 - github.com/yaricom/goNEAT/v4 v4.0.2 + github.com/yaricom/goGraphML v1.4.3 + github.com/yaricom/goNEAT/v4 v4.1.0 + gonum.org/v1/gonum v0.15.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -15,6 +16,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sbinet/npyio v0.8.0 // indirect github.com/spf13/cast v1.5.1 // indirect - golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect - gonum.org/v1/gonum v0.14.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect ) diff --git a/go.sum b/go.sum index 4408f0d..2f434d5 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -29,14 +29,14 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/yaricom/goGraphML v1.4.1 h1:ZT2yWzKeVx8dbiOZT+tBUbmCwDfWAG70HjavSWajTeY= -github.com/yaricom/goGraphML v1.4.1/go.mod h1:WdO/4yppeN4Ly/dnr5/Z+d/1UoQ8SA8r+x0IQ6/jBbE= -github.com/yaricom/goNEAT/v4 v4.0.2 h1:+XtgXI377ouYXWSRJGgFCoXUTeENSogdnxYq5USq2fQ= -github.com/yaricom/goNEAT/v4 v4.0.2/go.mod h1:/tekyUuX/P14WrL5aL/kgbDE4rB/rRJtpCS8OSp4UB4= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= -gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= +github.com/yaricom/goGraphML v1.4.3 h1:8S8Q7zH56Ot4owhMX6ElgHFxgDlaHDXJZf4P1vm1eB8= +github.com/yaricom/goGraphML v1.4.3/go.mod h1:WdO/4yppeN4Ly/dnr5/Z+d/1UoQ8SA8r+x0IQ6/jBbE= +github.com/yaricom/goNEAT/v4 v4.1.0 h1:UdNqvDtRxafNcFvDPOqsg0bHjdc20deQF434nAC/Ar8= +github.com/yaricom/goNEAT/v4 v4.1.0/go.mod h1:/tekyUuX/P14WrL5aL/kgbDE4rB/rRJtpCS8OSp4UB4= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= +gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hyperneat/hyper_neat.go b/hyperneat/hyper_neat.go index db05020..e52b4ac 100644 --- a/hyperneat/hyper_neat.go +++ b/hyperneat/hyper_neat.go @@ -6,7 +6,6 @@ import ( "github.com/yaricom/goNEAT/v4/neat/math" "gopkg.in/yaml.v3" "io" - "io/ioutil" "os" ) @@ -40,7 +39,7 @@ type Options struct { // LoadYAMLOptions is to read HyperNEAT options from the provided reader func LoadYAMLOptions(r io.Reader) (*Options, error) { - content, err := ioutil.ReadAll(r) + content, err := io.ReadAll(r) if err != nil { return nil, err } From 25b46a254b49b7dac48058e222ea6d12ab3e8ab4 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 16 May 2024 19:32:56 +0300 Subject: [PATCH 49/57] Changed retina solver activation routine --- examples/retina/retina.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/retina/retina.go b/examples/retina/retina.go index bac27a1..4ba5655 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -179,7 +179,7 @@ func (e *generationEvaluator) organismEvaluate(ctx context.Context, organism *ge if !ok { return false, nil, eshyperneat.ErrESHyperNEATOptionsNotFound } - // get CPPN network solver + // get CPPN phenotype network cppnSolver, err := organism.Phenotype() if err != nil { return false, nil, errors.Wrap(err, "failed to create CPPN solver") @@ -267,16 +267,18 @@ func evaluateNetwork(solver network.Solver, leftObj VisualObject, rightObj Visua inputs := append(leftObj.data, rightObj.data...) // run evaluation - loss := math.MaxFloat64 + loss := math.MaxFloat32 if err := solver.LoadSensors(inputs); err != nil { return loss, err } // Propagate activation - if relaxed, err := solver.RecursiveSteps(); err != nil { + activationSteps := 1000 + if relaxed, err := solver.Relax(activationSteps, 0.1); err != nil { return loss, err } else if !relaxed { - return loss, errors.New("failed to relax network solver of the ES substrate") + neat.DebugLog("failed to relax network solver of the ES substrate") + return loss, nil } // get outputs and evaluate against ground truth From 54552e370c5f86c98aad36169cab4311c08af3ad Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Thu, 16 May 2024 19:34:50 +0300 Subject: [PATCH 50/57] Modified evolvable_substrate.go to support CPPN as Network --- cppn/cppn.go | 42 ++++++++++++++++++++++++++++++++++++- cppn/evolvable_substrate.go | 4 ++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/cppn/cppn.go b/cppn/cppn.go index 69d88d3..ae5af26 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -4,6 +4,7 @@ package cppn import ( "errors" + "github.com/yaricom/goNEAT/v4/neat" "github.com/yaricom/goNEAT/v4/neat/genetics" "github.com/yaricom/goNEAT/v4/neat/network" "gonum.org/v1/gonum/stat" @@ -12,6 +13,15 @@ import ( // FastSolverFromGenomeFile Reads CPPN from specified genome and creates network solver func FastSolverFromGenomeFile(genomePath string) (network.Solver, error) { + if net, err := NetworkFromGenomeFile(genomePath); err != nil { + return nil, err + } else { + return net, nil + } +} + +// NetworkFromGenomeFile Reads CPPN from specified genome and creates phenotype network +func NetworkFromGenomeFile(genomePath string) (*network.Network, error) { if reader, err := genetics.NewGenomeReaderFromFile(genomePath); err != nil { return nil, err } else if genome, err := reader.Read(); err != nil { @@ -19,7 +29,7 @@ func FastSolverFromGenomeFile(genomePath string) (network.Solver, error) { } else if net, err := genome.Genesis(genome.Id); err != nil { return nil, err } else { - return net.FastNetworkSolver() + return net, nil } } @@ -72,6 +82,36 @@ func queryCPPN(coordinates []float64, cppn network.Solver) ([]float64, error) { return cppn.ReadOutputs(), nil } +func queryCPPNNetwork(coordinates []float64, cppn *network.Network) ([]float64, error) { + if res, err := cppn.Flush(); err != nil { + return nil, err + } else if !res { + return nil, errors.New("failed to flush CPPN network") + } + + // load inputs + if err := cppn.LoadSensors(coordinates); err != nil { + return nil, err + } + + // do activations + maxDepth, err := cppn.MaxActivationDepth() + if err != nil { + neat.WarnLog("Failed to calculate max activation depth for CPPN network") + } + if maxDepth == 0 { + neat.WarnLog("Max activation depth for CPPN network is zero, setting default value") + maxDepth = 100 + } + if res, err := cppn.ForwardSteps(maxDepth); err != nil { + return nil, err + } else if !res { + return nil, errors.New("failed to relax CPPN network recursively") + } + + return cppn.ReadOutputs(), nil +} + // Determines variance among CPPN values for certain hypercube region around specified node. // This variance is a heuristic indicator of the heterogeneity (i.e. presence of information) of a region. func nodeVariance(node *QuadNode) float64 { diff --git a/cppn/evolvable_substrate.go b/cppn/evolvable_substrate.go index b65567f..8fdcf2f 100644 --- a/cppn/evolvable_substrate.go +++ b/cppn/evolvable_substrate.go @@ -24,7 +24,7 @@ type EvolvableSubstrate struct { OutputNodesActivation neatmath.NodeActivationType // The CPPN network solver to describe geometry of substrate - cppn network.Solver + cppn *network.Network // The reusable coordinates buffer coords []float64 } @@ -55,7 +55,7 @@ func NewEvolvableSubstrateWithBias(layout EvolvableSubstrateLayout, hiddenNodesA // CreateNetworkSolver Creates network solver based on current substrate layout and provided Compositional Pattern Producing Network which // used to define connections between network nodes. Optional graph_builder can be provided to collect graph nodes and edges // of created network solver. With graph builder it is possible to save/load network configuration as well as visualize it. -func (es *EvolvableSubstrate) CreateNetworkSolver(cppn network.Solver, graphBuilder SubstrateGraphBuilder, options *eshyperneat.Options) (network.Solver, error) { +func (es *EvolvableSubstrate) CreateNetworkSolver(cppn *network.Network, graphBuilder SubstrateGraphBuilder, options *eshyperneat.Options) (network.Solver, error) { es.cppn = cppn // the network layers will be collected in order: bias, input, output, hidden From c384a240a50e6c69025211f4d5bf8729bafbee74 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 9 Dec 2024 18:00:09 +0200 Subject: [PATCH 51/57] Updated README to include correct links in references. Updated dependencies. --- README.md | 22 +++++++++++----------- go.mod | 6 +++--- go.sum | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index aa5ecf4..255a4ea 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ by human before algorithm execution to reflect inherent geometrical topology of of hidden nodes in the network this leads to the reduction of algorithm efficiency due to lack of ability to estimate where CPPN generated patterns will have intersection with manually seeded nodes. -This drawback is addressed by **Evolved-Substrate HyperNEAT** method which allows to encode hidden nodes position +This drawback is addressed by **Evolvable-Substrate HyperNEAT** method which allows to encode hidden nodes position in the substrate in the CPPN generated patterns of weights. As additional benefit of this the substrate is able to evolve it's geometrical topology during training, producing regions with varying neural density, thereby providing a kind of scaffolding for situating cognitive structures in the biological brains. @@ -28,11 +28,11 @@ for situating cognitive structures in the biological brains. 1. The original C++ NEAT implementation created by Kenneth O. Stanley, see: [NEAT][1] 2. Other NEAT implementations can be found at [NEAT Software Catalog][2] 3. [The ES-HyperNEAT Users Page][3] -4. Kenneth O. Stanley, David D’Ambrosio and Jason Gauci, [A Hypercube-Based Indirect Encoding for Evolving Large-Scale Neural Networks][4], Artificial Life journal 15(2), Cambridge, MA: MIT Press, 2009 -5. Sebastian Risi, Kenneth O. Stanley, [An Enhanced Hypercube-Based Encoding for Evolving the Placement, Density and Connectivity of Neurons][5], Artificial Life journal, Cambridge, MA: MIT Press, 2012 -6. Kenneth O. Stanley, [Ph.D. Dissertation: EFFICIENT EVOLUTION OF NEURAL NETWORKS THROUGH COMPLEXIFICATION][6], Department of Computer Sciences, The University of Texas at Austin, Technical Report~AI-TR-04–39, August 2004 -7. Kenneth O. Stanley, [Compositional Pattern Producing Networks: A Novel Abstraction of Development][7], Genetic Programming and Evolvable Machines, Special Issue on Developmental Systems, New York, NY: Springer, 2007 -8. Iaroslav Omelianenko, [The GoLang NEAT implementation][8], GitHub, 2018 +4. Kenneth O. Stanley, David D’Ambrosio and Jason Gauci, [A Hypercube-Based Indirect Encoding for Evolving Large-Scale Neural Networks][4], Artificial Life journal, Cambridge, MA: MIT Press, 2009, vol. 15, no. 2, pp. 185-212 +5. Sebastian Risi, Kenneth O. Stanley, [An Enhanced Hypercube-Based Encoding for Evolving the Placement, Density and Connectivity of Neurons][5], Artificial Life journal, Cambridge, MA: MIT Press, 2012, vol. 18, no. 4, pp. 331-363 +6. Kenneth O. Stanley, [Ph.D. Dissertation: Efficient Evolution of Neural Networks through Complexification][6], Department of Computer Sciences, The University of Texas at Austin, Technical Report~AI-TR-04–39, August 2004 +7. Kenneth O. Stanley, [Compositional Pattern Producing Networks: A Novel Abstraction of Development][7], Genetic Programming and Evolvable Machines, Special Issue on Developmental Systems, New York, NY: Springer, 2007, vol. 8, pp. 131-162 +8. Omelianenko, Iaroslav, [The GoLang implementation of NeuroEvolution of Augmented Topologies (NEAT) algorithm][8], GitHub, Computer software This source code maintained and managed by [Iaroslav Omelianenko][9] @@ -40,9 +40,9 @@ This source code maintained and managed by [Iaroslav Omelianenko][9] [1]:http://www.cs.ucf.edu/~kstanley/neat.html [2]:http://eplex.cs.ucf.edu/neat_software/ [3]:http://eplex.cs.ucf.edu/hyperNEATpage/HyperNEAT.html -[4]:http://eplex.cs.ucf.edu/papers/stanley_alife09.pdf -[5]:https://www.mitpressjournals.org/doi/pdfplus/10.1162/ARTL_a_00071 -[6]:http://nn.cs.utexas.edu/keyword?stanley:phd04 -[7]:http://eplex.cs.ucf.edu/papers/stanley_gpem07.pdf -[8]:https://github.com/yaricom/goNEAT +[4]:https://doi.org/10.1162/artl.2009.15.2.15202 +[5]:https://doi.org/10.1162/ARTL_a_00071 +[6]:https://nn.cs.utexas.edu/downloads/papers/stanley.phd04.pdf +[7]:https://doi.org/10.1007/s10710-007-9028-8 +[8]:https://doi.org/10.5281/zenodo.13628842 [9]:https://io42.space \ No newline at end of file diff --git a/go.mod b/go.mod index 4b735c6..7591d84 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,10 @@ go 1.21 require ( github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/yaricom/goGraphML v1.4.3 - github.com/yaricom/goNEAT/v4 v4.1.0 - gonum.org/v1/gonum v0.15.0 + github.com/yaricom/goNEAT/v4 v4.2.0 + gonum.org/v1/gonum v0.14.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 2f434d5..6667a7d 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -27,16 +27,16 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yaricom/goGraphML v1.4.3 h1:8S8Q7zH56Ot4owhMX6ElgHFxgDlaHDXJZf4P1vm1eB8= github.com/yaricom/goGraphML v1.4.3/go.mod h1:WdO/4yppeN4Ly/dnr5/Z+d/1UoQ8SA8r+x0IQ6/jBbE= -github.com/yaricom/goNEAT/v4 v4.1.0 h1:UdNqvDtRxafNcFvDPOqsg0bHjdc20deQF434nAC/Ar8= -github.com/yaricom/goNEAT/v4 v4.1.0/go.mod h1:/tekyUuX/P14WrL5aL/kgbDE4rB/rRJtpCS8OSp4UB4= +github.com/yaricom/goNEAT/v4 v4.2.0 h1:ms7Tjg//rl54SbkpML6KbWmDXJul04sqPxc3zBFIuy8= +github.com/yaricom/goNEAT/v4 v4.2.0/go.mod h1:/tekyUuX/P14WrL5aL/kgbDE4rB/rRJtpCS8OSp4UB4= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= -gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= -gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From cac46177dc82ec1fa1c449c9185d5f7033efedeb Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 9 Dec 2024 18:19:24 +0200 Subject: [PATCH 52/57] Updated GitHub workflows --- .github/workflows/ci.yml | 9 +++++++-- .github/workflows/super-linter.yml | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d17d869..5ef4fac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.21 - name: Build run: go build -v ./... @@ -29,4 +29,9 @@ jobs: GO111MODULE: on - name: Upload coverage to Codecov - run: bash <(curl -s https://codecov.io/bash) + uses: codecov/codecov-action@v4 + with: + files: ./coverage.txt + flags: unittests + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 3a458a0..f6230cb 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -22,15 +22,15 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: - go-version: 1.17 - - uses: actions/checkout@v3 + go-version: '1.21' + - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: v1.29 + version: v1.60 # Optional: working directory, useful for monorepos # working-directory: somedir From fa6f17f9b7c75d807e49cf93e157b6cc33c1345b Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 9 Dec 2024 18:20:22 +0200 Subject: [PATCH 53/57] Retina experiment configuration updates. --- cppn/quad_tree.go | 3 ++- data/retina/cppn_genome.yml | 26 +++++++++++++------------- data/retina/es_hyper.neat.yml | 4 ++-- examples/retina/dataset.go | 18 +++++++++--------- examples/retina/retina.go | 4 ++-- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/cppn/quad_tree.go b/cppn/quad_tree.go index 2d6353c..33d480e 100644 --- a/cppn/quad_tree.go +++ b/cppn/quad_tree.go @@ -70,7 +70,8 @@ func (q *QuadNode) Leo() float64 { if len(q.CppnOut) > 1 { return q.CppnOut[1] } - return -1.0 + // if no LEO output - enable link unconditionally + return 1.0 } func (q *QuadNode) HasLeo() bool { diff --git a/data/retina/cppn_genome.yml b/data/retina/cppn_genome.yml index bbfd1d1..e88fd1e 100644 --- a/data/retina/cppn_genome.yml +++ b/data/retina/cppn_genome.yml @@ -22,21 +22,21 @@ genome: - { id: 5, trait_id: 0, type: INPT, activation: LinearActivation } # x2 - { id: 6, trait_id: 0, type: INPT, activation: LinearActivation } # y2 - { id: 7, trait_id: 0, type: INPT, activation: LinearActivation } # z2 - # The output nodes - actuators TANH to make sure we have output values in range (-1, 1) - - { id: 8, trait_id: 0, type: OUTP, activation: TanhActivation } + # The output nodes - actuators SigmoidBipolarActivation to make sure we have output values in range (-1, 1) + - { id: 8, trait_id: 0, type: OUTP, activation: SigmoidBipolarActivation } # The hidden node - - { id: 9, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G1 - - { id: 10, trait_id: 0, type: HIDN, activation: GaussianBipolarActivation } # G2 + - { id: 9, trait_id: 0, type: HIDN, activation: GaussianActivation } # G1 + - { id: 10, trait_id: 0, type: HIDN, activation: GaussianActivation } # G2 + # LEO + - { id: 11, trait_id: 0, type: OUTP, activation: StepActivation } # The genes - connection between neuron nodes within this genome genes: - - { src_id: 1, tgt_id: 9, weight: 0.5, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 1, tgt_id: 10, weight: 0.5, trait_id: 6, innov_num: 2, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 1, tgt_id: 10, weight: 0.5, trait_id: 1, innov_num: 1, mut_num: 0, recurrent: false, enabled: true } - { src_id: 2, tgt_id: 9, weight: 0.5, trait_id: 2, innov_num: 3, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 3, tgt_id: 9, weight: 0.5, trait_id: 3, innov_num: 4, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 4, tgt_id: 9, weight: 0.5, trait_id: 4, innov_num: 5, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 5, tgt_id: 10, weight: 0.5, trait_id: 5, innov_num: 6, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 6, tgt_id: 10, weight: 0.5, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 7, tgt_id: 10, weight: 0.5, trait_id: 7, innov_num: 8, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 9, tgt_id: 8, weight: 0.5, trait_id: 8, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } - - { src_id: 10, tgt_id: 8, weight: 0.5, trait_id: 8, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } \ No newline at end of file + - { src_id: 3, tgt_id: 10, weight: 0.5, trait_id: 3, innov_num: 4, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 5, tgt_id: 9, weight: 0.5, trait_id: 5, innov_num: 6, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 6, tgt_id: 10, weight: 0.5, trait_id: 7, innov_num: 7, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 9, tgt_id: 11, weight: 0.5, trait_id: 8, innov_num: 9, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 10, tgt_id: 8, weight: 0.5, trait_id: 8, innov_num: 10, mut_num: 0, recurrent: false, enabled: true } + - { src_id: 1, tgt_id: 11, weight: 0.5, trait_id: 8, innov_num: 11, mut_num: 0, recurrent: false, enabled: true } \ No newline at end of file diff --git a/data/retina/es_hyper.neat.yml b/data/retina/es_hyper.neat.yml index 6db9b17..4cc666a 100644 --- a/data/retina/es_hyper.neat.yml +++ b/data/retina/es_hyper.neat.yml @@ -79,7 +79,7 @@ print_every: 10 # The number of runs to average over in an experiment num_runs: 1 # The number of epochs (generations) to execute training -num_generations: 100 #1500 +num_generations: 1500 # The epoch's executor type to apply [sequential, parallel] epoch_executor: sequential @@ -104,7 +104,7 @@ node_activators: link_threshold: 0.0 # Indicates whether Link Expression Output (LEO) enabled -leo_enabled: false +leo_enabled: true # The weight range defines the minimum and maximum values for weights on substrate connections weight_range: 1 diff --git a/examples/retina/dataset.go b/examples/retina/dataset.go index 982c745..87b5d68 100644 --- a/examples/retina/dataset.go +++ b/examples/retina/dataset.go @@ -6,22 +6,22 @@ func CreateRetinaDataset() []VisualObject { // set left side objects objs = append(objs, NewVisualObject(BothSide, ". .\n. .")) objs = append(objs, NewVisualObject(BothSide, ". .\n. o")) - objs = append(objs, NewVisualObject(LeftSide, ". o\n. o")) - objs = append(objs, NewVisualObject(BothSide, ". o\n. .")) - objs = append(objs, NewVisualObject(LeftSide, ". o\no o")) objs = append(objs, NewVisualObject(BothSide, ". .\no .")) - objs = append(objs, NewVisualObject(LeftSide, "o o\n. o")) + objs = append(objs, NewVisualObject(BothSide, ". o\n. .")) objs = append(objs, NewVisualObject(BothSide, "o .\n. .")) + objs = append(objs, NewVisualObject(LeftSide, ". .\no o")) + objs = append(objs, NewVisualObject(LeftSide, ". o\no o")) + objs = append(objs, NewVisualObject(LeftSide, "o .\no o")) // set right side objects objs = append(objs, NewVisualObject(BothSide, ". .\n. .")) - objs = append(objs, NewVisualObject(BothSide, "o .\n. .")) - objs = append(objs, NewVisualObject(RightSide, "o .\no .")) + objs = append(objs, NewVisualObject(BothSide, ". .\n. o")) objs = append(objs, NewVisualObject(BothSide, ". .\no .")) - objs = append(objs, NewVisualObject(RightSide, "o o\no .")) objs = append(objs, NewVisualObject(BothSide, ". o\n. .")) - objs = append(objs, NewVisualObject(RightSide, "o .\no o")) - objs = append(objs, NewVisualObject(BothSide, ". .\n. o")) + objs = append(objs, NewVisualObject(BothSide, "o .\n. .")) + objs = append(objs, NewVisualObject(RightSide, "o o\n. .")) + objs = append(objs, NewVisualObject(RightSide, "o o\no .")) + objs = append(objs, NewVisualObject(RightSide, "o o\n. o")) return objs } diff --git a/examples/retina/retina.go b/examples/retina/retina.go index 4ba5655..08bab42 100644 --- a/examples/retina/retina.go +++ b/examples/retina/retina.go @@ -20,7 +20,7 @@ import ( const ( // maxFitness Used as max value which we add error too to get an organism's fitness - maxFitness = 256.0 + maxFitness = 1000.0 // fitnessThreshold is the fitness value for which an organism is considered to have won the experiment fitnessThreshold = maxFitness @@ -267,7 +267,7 @@ func evaluateNetwork(solver network.Solver, leftObj VisualObject, rightObj Visua inputs := append(leftObj.data, rightObj.data...) // run evaluation - loss := math.MaxFloat32 + loss := maxFitness if err := solver.LoadSensors(inputs); err != nil { return loss, err } From 9302962280b6e192464599f361756d46293f1eaa Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 9 Dec 2024 18:34:44 +0200 Subject: [PATCH 54/57] Fixed failing unit tests. --- cppn/cppn_test.go | 4 ++-- cppn/evolvable_substrate_test.go | 8 ++++---- cppn/substrate_test.go | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cppn/cppn_test.go b/cppn/cppn_test.go index 54d52a1..ff16280 100644 --- a/cppn/cppn_test.go +++ b/cppn/cppn_test.go @@ -43,7 +43,7 @@ func TestFastSolverFromGenomeFile(t *testing.T) { require.NoError(t, err, "failed to query CPPN") require.NotNil(t, outs, "output expected") require.Len(t, outs, 1) - assert.InDelta(t, 0.5109358170373398, outs[0], 1e-16, "wrong output value") + assert.InDelta(t, 0.566, outs[0], 1e-3, "wrong output value") } func TestFastSolverFromGenomeFile_LEO(t *testing.T) { @@ -60,7 +60,7 @@ func TestFastSolverFromGenomeFile_LEO(t *testing.T) { require.NotNil(t, outs, "output expected") require.Len(t, outs, 2) t.Log(outs) - assert.InDelta(t, 0.15114695049520718, outs[0], 1e-16, "wrong output value") + assert.InDelta(t, 0.048, outs[0], 1e-3, "wrong output value") assert.Equal(t, 1.0, outs[1], "wrong LEO value") } diff --git a/cppn/evolvable_substrate_test.go b/cppn/evolvable_substrate_test.go index 7649dac..dac4d16 100644 --- a/cppn/evolvable_substrate_test.go +++ b/cppn/evolvable_substrate_test.go @@ -17,7 +17,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) - cppn, err := FastSolverFromGenomeFile(cppnHyperNEATTestGenomePath) + cppn, err := NetworkFromGenomeFile(cppnHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") context, err := loadESHyperNeatOptions(esHyperNeatTestConfigFile) require.NoError(t, err, "failed to read ESHyperNEAT context") @@ -31,7 +31,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver(t *testing.T) { totalNodeCount := inputCount + outputCount + layout.HiddenCount() assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong total node count") - assert.Equal(t, 30, solver.LinkCount(), "wrong link number") + assert.Equal(t, 27, solver.LinkCount(), "wrong link number") // check outputs outExpected := []float64{0, 0} @@ -45,7 +45,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { substr := NewEvolvableSubstrate(layout, math.SigmoidSteepenedActivation, math.LinearActivation) - cppn, err := FastSolverFromGenomeFile(cppnLeoHyperNEATTestGenomePath) + cppn, err := NetworkFromGenomeFile(cppnLeoHyperNEATTestGenomePath) require.NoError(t, err, "failed to read CPPN") context, err := loadESHyperNeatOptions(esHyperNeatTestConfigFile) require.NoError(t, err, "failed to read ESHyperNEAT context") @@ -60,7 +60,7 @@ func TestEvolvableSubstrate_CreateNetworkSolver_LEO(t *testing.T) { totalNodeCount := inputCount + outputCount + layout.HiddenCount() assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong total node count") - assert.Equal(t, 12, solver.LinkCount(), "wrong link number") + assert.Equal(t, 5, solver.LinkCount(), "wrong link number") // check outputs outExpected := []float64{0, 0} diff --git a/cppn/substrate_test.go b/cppn/substrate_test.go index 8e6215f..165df2c 100644 --- a/cppn/substrate_test.go +++ b/cppn/substrate_test.go @@ -44,11 +44,11 @@ func TestSubstrate_CreateNetworkSolver(t *testing.T) { totalNodeCount := biasCount + inputCount + hiddenCount + outputCount assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong nodes number") - totalLinkCount := 13 + totalLinkCount := 12 assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - outExpected := []float64{0.12926155695140656, 0.15786889573150728} + outExpected := []float64{1.0768005629123314, 1.131042391465084} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } @@ -78,11 +78,11 @@ func TestSubstrate_CreateLEONetworkSolver(t *testing.T) { totalNodeCount := biasCount + inputCount + hiddenCount + outputCount assert.Equal(t, totalNodeCount, solver.NodeCount(), "wrong nodes number") - totalLinkCount := 15 + totalLinkCount := 16 assert.Equal(t, totalLinkCount, solver.LinkCount(), "wrong links number") // test outputs - outExpected := []float64{0.33812764749536894, -0.017572705522999554} + outExpected := []float64{-0.1147701149321737, -0.7584174401602518} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } @@ -113,7 +113,7 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { totalNodes := biasCount + inputCount + hiddenCount + outputCount assert.Len(t, graph.Nodes, totalNodes, "wrong nodes number") - totalEdges := 13 + totalEdges := 12 assert.Len(t, graph.Edges, totalEdges, "wrong edges number") var buf bytes.Buffer @@ -121,10 +121,10 @@ func TestSubstrate_CreateNetworkSolverWithGraphBuilder(t *testing.T) { require.NoError(t, err, "failed to marshal graph") strOut := buf.String() - assert.Equal(t, 5789, len(strOut), "wrong length of marshalled string") + assert.Equal(t, 5587, len(strOut), "wrong length of marshalled string") // test outputs - outExpected := []float64{0.12926155695140656, 0.15786889573150728} + outExpected := []float64{1.0768005629123314, 1.131042391465084} checkNetworkSolverOutputs(solver, outExpected, 0.0, t) } From 011dd11ddc922314ae558edd73d4e145c9016936 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 9 Dec 2024 18:55:41 +0200 Subject: [PATCH 55/57] Updated to use `ReadAll` from `io` package. Commented unused method --- cppn/cppn.go | 59 ++++++++++++++++++------------------ eshyperneat/es_hyper_neat.go | 3 +- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/cppn/cppn.go b/cppn/cppn.go index ae5af26..3157d0a 100644 --- a/cppn/cppn.go +++ b/cppn/cppn.go @@ -4,7 +4,6 @@ package cppn import ( "errors" - "github.com/yaricom/goNEAT/v4/neat" "github.com/yaricom/goNEAT/v4/neat/genetics" "github.com/yaricom/goNEAT/v4/neat/network" "gonum.org/v1/gonum/stat" @@ -82,35 +81,35 @@ func queryCPPN(coordinates []float64, cppn network.Solver) ([]float64, error) { return cppn.ReadOutputs(), nil } -func queryCPPNNetwork(coordinates []float64, cppn *network.Network) ([]float64, error) { - if res, err := cppn.Flush(); err != nil { - return nil, err - } else if !res { - return nil, errors.New("failed to flush CPPN network") - } - - // load inputs - if err := cppn.LoadSensors(coordinates); err != nil { - return nil, err - } - - // do activations - maxDepth, err := cppn.MaxActivationDepth() - if err != nil { - neat.WarnLog("Failed to calculate max activation depth for CPPN network") - } - if maxDepth == 0 { - neat.WarnLog("Max activation depth for CPPN network is zero, setting default value") - maxDepth = 100 - } - if res, err := cppn.ForwardSteps(maxDepth); err != nil { - return nil, err - } else if !res { - return nil, errors.New("failed to relax CPPN network recursively") - } - - return cppn.ReadOutputs(), nil -} +//func queryCPPNNetwork(coordinates []float64, cppn *network.Network) ([]float64, error) { +// if res, err := cppn.Flush(); err != nil { +// return nil, err +// } else if !res { +// return nil, errors.New("failed to flush CPPN network") +// } +// +// // load inputs +// if err := cppn.LoadSensors(coordinates); err != nil { +// return nil, err +// } +// +// // do activations +// maxDepth, err := cppn.MaxActivationDepth() +// if err != nil { +// neat.WarnLog("Failed to calculate max activation depth for CPPN network") +// } +// if maxDepth == 0 { +// neat.WarnLog("Max activation depth for CPPN network is zero, setting default value") +// maxDepth = 100 +// } +// if res, err := cppn.ForwardSteps(maxDepth); err != nil { +// return nil, err +// } else if !res { +// return nil, errors.New("failed to relax CPPN network recursively") +// } +// +// return cppn.ReadOutputs(), nil +//} // Determines variance among CPPN values for certain hypercube region around specified node. // This variance is a heuristic indicator of the heterogeneity (i.e. presence of information) of a region. diff --git a/eshyperneat/es_hyper_neat.go b/eshyperneat/es_hyper_neat.go index f334306..6307f00 100644 --- a/eshyperneat/es_hyper_neat.go +++ b/eshyperneat/es_hyper_neat.go @@ -6,7 +6,6 @@ import ( "github.com/yaricom/goESHyperNEAT/v2/hyperneat" "gopkg.in/yaml.v3" "io" - "io/ioutil" "os" ) @@ -44,7 +43,7 @@ type Options struct { // LoadYAMLOptions is to load ES-HyperNEAT options from provided reader func LoadYAMLOptions(r io.Reader) (*Options, error) { - content, err := ioutil.ReadAll(r) + content, err := io.ReadAll(r) if err != nil { return nil, err } From 8d21ae96c6bb936130c0824a068cf53049685287 Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 9 Dec 2024 18:56:04 +0200 Subject: [PATCH 56/57] Changed GO version for linter --- .github/workflows/super-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index f6230cb..1e43d72 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/setup-go@v5 with: - go-version: '1.21' + go-version: '1.19' - uses: actions/checkout@v4 - name: golangci-lint uses: golangci/golangci-lint-action@v6 From 632ee17a05d7cdc0c550236e179c92bee59830bb Mon Sep 17 00:00:00 2001 From: Iaroslav Omelianenko Date: Mon, 9 Dec 2024 19:17:42 +0200 Subject: [PATCH 57/57] Updated workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ef4fac..9cf0118 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v3