Пример #1
0
// Adds a new node to the genome
//
// In the add node mutation, an existing connection is split and the new node placed where the old
// connection used to be. The old connection is disabled and two new connections are added to the
// genome. The connection between the first node in the chain and the new node is given a weight
// of one, and the connection between the new node and the last node in the chain is given the
// same weight as the connection being split. Splitting the connection in this way introduces a
// nonlinearity (i.e. sigmoid function) where there was none before. Because the new node is
// immediately integrated into the network, its effect on fitness can be evaluated right away.
// Preexisting network structure is not destroyed and performs the same function, while the new
// structure provides an opportunity to elaborate on the original behaviors. (Stanley, 35)
func (m *Complexify) addNode(rng *rand.Rand, g *neat.Genome) {

	// Pick a connection to split
	var inno int
	var c0 neat.Connection
	found := false
	for k, conn := range g.Conns {
		c0 = conn
		inno = k

		// Ensure resultant node doesn't already exist
		found = true
		src := g.Nodes[c0.Source]
		tgt := g.Nodes[c0.Target]
		x := (src.X + tgt.X) / 2.0
		y := (src.Y + tgt.Y) / 2.0
		for _, node := range g.Nodes {
			if node.X == x && node.Y == y {
				found = false
				break
			}
		}
		if found {
			break
		}
	}
	if !found {
		return
	}
	c0.Enabled = false
	g.Conns[inno] = c0

	// Add the new node
	src := g.Nodes[c0.Source]
	tgt := g.Nodes[c0.Target]
	n0 := neat.Node{NeuronType: neat.Hidden, ActivationType: m.HiddenActivation(), X: (src.X + tgt.X) / 2.0, Y: (src.Y + tgt.Y) / 2.0}
	n0.Innovation = m.ctx.Innovation(neat.NodeInnovation, n0.Key())
	g.Nodes[n0.Innovation] = n0

	// Add the new connections
	c1 := neat.Connection{Source: src.Innovation, Target: n0.Innovation, Enabled: true, Weight: 1.0}
	c1.Innovation = m.ctx.Innovation(neat.ConnInnovation, c1.Key())
	g.Conns[c1.Innovation] = c1

	c2 := neat.Connection{Source: n0.Innovation, Target: tgt.Innovation, Enabled: true, Weight: c0.Weight}
	c2.Innovation = m.ctx.Innovation(neat.ConnInnovation, c2.Key())
	g.Conns[c2.Innovation] = c2
}
Пример #2
0
// Returns a genome build from the parameters
func createSeed(ctx neat.Context, cfg ClassicSettings) (adam neat.Genome) {
	// Create the genome
	inputs := cfg.NumInputs()
	outputs := cfg.NumOutputs()
	adam = neat.Genome{
		Nodes: make(map[int]neat.Node, 1+inputs+outputs),
	}
	nodes := make([]neat.Node, len(adam.Nodes))
	node := neat.Node{NeuronType: neat.Bias, ActivationType: neat.Direct, X: 0, Y: 0}
	node.Innovation = ctx.Innovation(neat.NodeInnovation, node.Key())
	adam.Nodes[node.Innovation] = node
	nodes = append(nodes, node)
	for i := 0; i < inputs; i++ {
		node = neat.Node{NeuronType: neat.Input, ActivationType: neat.Direct, X: float64(i+1) / float64(inputs), Y: 0}
		node.Innovation = ctx.Innovation(neat.NodeInnovation, node.Key())
		adam.Nodes[node.Innovation] = node
		nodes = append(nodes, node)
	}
	x := 0.5
	for i := 0; i < outputs; i++ {
		if outputs > 1 {
			x = float64(i) / float64(outputs-1)
		}
		node = neat.Node{NeuronType: neat.Output, ActivationType: cfg.OutputActivation(), X: x, Y: 1}
		node.Innovation = ctx.Innovation(neat.NodeInnovation, node.Key())
		adam.Nodes[node.Innovation] = node
		nodes = append(nodes, node)
	}

	rng := rand.New(rand.NewSource(rand.Int63()))
	// adam.Conns = make(map[int]neat.Connection, (1+inputs)*outputs)
	// for i := 0; i < 1+inputs; i++ {
	// 	for j := 0; j < outputs; j++ {
	// 		w := (rng.Float64()*2.0 - 1.0) * cfg.WeightRange()
	// 		conn := neat.Connection{Source: nodes[i].Innovation, Target: nodes[j+1+inputs].Innovation, Enabled: true, Weight: w}
	// 		conn.Innovation = ctx.Innovation(neat.ConnInnovation, conn.Key())
	// 		adam.Conns[conn.Innovation] = conn
	// 	}
	// }

	ts := cfg.Traits()
	adam.Traits = make([]float64, len(ts))
	for i, trait := range ts {
		adam.Traits[i] = rng.Float64()*(trait.Max-trait.Min) + trait.Min // TODO: Get setting values from configuration
	}
	return adam
}