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

     1  package network
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/yaricom/goNEAT/neat"
     7  	"github.com/yaricom/goNEAT/neat/utils"
     8  )
     9  
    10  // A NODE is either a NEURON or a SENSOR.
    11  //   - If it's a sensor, it can be loaded with a value for output
    12  //   - If it's a neuron, it has a list of its incoming input signals ([]*Link is used)
    13  // Use an activation count to avoid flushing
    14  type NNode struct {
    15  	// The ID of the node
    16  	Id int
    17  
    18  	// The type of node activation function (SIGMOID, ...)
    19  	ActivationType utils.NodeActivationType
    20  	// The neuron type for this node (HIDDEN, INPUT, OUTPUT, BIAS)
    21  	NeuronType NodeNeuronType
    22  
    23  	// The node's activation value
    24  	Activation float64
    25  	// The number of activations for current node
    26  	ActivationsCount int32
    27  	// The activation sum
    28  	ActivationSum float64
    29  
    30  	// The list of all incoming connections
    31  	Incoming []*Link
    32  	// The list of all outgoing connections
    33  	Outgoing []*Link
    34  	// The trait linked to the node
    35  	Trait *neat.Trait
    36  
    37  	// Used for Gene decoding by referencing analogue to this node in organism phenotype
    38  	PhenotypeAnalogue *NNode
    39  
    40  	// the flag to use for loop detection
    41  	visited bool
    42  
    43  	/* ************ LEARNING PARAMETERS *********** */
    44  	// The following parameters are for use in neurons that learn through habituation,
    45  	// sensitization, or Hebbian-type processes  */
    46  	Params []float64
    47  
    48  	// Activation value of node at time t-1; Holds the previous step's activation for recurrency
    49  	lastActivation float64
    50  	// Activation value of node at time t-2 Holds the activation before  the previous step's
    51  	// This is necessary for a special recurrent case when the innode of a recurrent link is one time step ahead of the outnode.
    52  	// The innode then needs to send from TWO time steps ago
    53  	lastActivation2 float64
    54  
    55  	// If true the node is active - used during node activation
    56  	isActive bool
    57  }
    58  
    59  // Creates new node with specified ID and neuron type associated (INPUT, HIDDEN, OUTPUT, BIAS)
    60  func NewNNode(nodeId int, neuronType NodeNeuronType) *NNode {
    61  	n := NewNetworkNode()
    62  	n.Id = nodeId
    63  	n.NeuronType = neuronType
    64  	return n
    65  }
    66  
    67  // Construct a NNode off another NNode with given trait for genome purposes
    68  func NewNNodeCopy(n *NNode, t *neat.Trait) *NNode {
    69  	node := NewNetworkNode()
    70  	node.Id = n.Id
    71  	node.NeuronType = n.NeuronType
    72  	node.ActivationType = n.ActivationType
    73  	node.Trait = t
    74  	return node
    75  }
    76  
    77  // The default constructor
    78  func NewNetworkNode() *NNode {
    79  	return &NNode{
    80  		NeuronType:     HiddenNeuron,
    81  		ActivationType: utils.SigmoidSteepenedActivation,
    82  		Incoming:       make([]*Link, 0),
    83  		Outgoing:       make([]*Link, 0),
    84  	}
    85  }
    86  
    87  // Set new activation value to this node
    88  func (n *NNode) setActivation(input float64) {
    89  	// Keep a memory of activations for potential time delayed connections
    90  	n.saveActivations()
    91  	// Set new activation value
    92  	n.Activation = input
    93  	// Increment the activation_count
    94  	n.ActivationsCount++
    95  }
    96  
    97  // Saves current node's activations for potential time delayed connections
    98  func (n *NNode) saveActivations() {
    99  	n.lastActivation2 = n.lastActivation
   100  	n.lastActivation = n.Activation
   101  }
   102  
   103  // Returns activation for a current step
   104  func (n *NNode) GetActiveOut() float64 {
   105  	if n.ActivationsCount > 0 {
   106  		return n.Activation
   107  	} else {
   108  		return 0.0
   109  	}
   110  }
   111  
   112  // Returns activation from PREVIOUS time step
   113  func (n *NNode) GetActiveOutTd() float64 {
   114  	if n.ActivationsCount > 1 {
   115  		return n.lastActivation
   116  	} else {
   117  		return 0.0
   118  	}
   119  }
   120  
   121  // Returns true if this node is SENSOR
   122  func (n *NNode) IsSensor() bool {
   123  	return n.NeuronType == InputNeuron || n.NeuronType == BiasNeuron
   124  }
   125  
   126  // returns true if this node is NEURON
   127  func (n *NNode) IsNeuron() bool {
   128  	return n.NeuronType == HiddenNeuron || n.NeuronType == OutputNeuron
   129  }
   130  
   131  // If the node is a SENSOR, returns TRUE and loads the value
   132  func (n *NNode) SensorLoad(load float64) bool {
   133  	if n.IsSensor() {
   134  		// Keep a memory of activations for potential time delayed connections
   135  		n.saveActivations()
   136  		// Puts sensor into next time-step
   137  		n.ActivationsCount++
   138  		n.Activation = load
   139  		return true
   140  	} else {
   141  		return false
   142  	}
   143  }
   144  
   145  // Adds a non recurrent outgoing link to this node
   146  func (n *NNode) addOutgoing(out *NNode, weight float64) {
   147  	newLink := NewLink(weight, n, out, false)
   148  	n.Outgoing = append(n.Outgoing, newLink)
   149  }
   150  
   151  // Adds a NONRECURRENT Link to an incoming NNode in the incoming List
   152  func (n *NNode) addIncoming(in *NNode, weight float64) {
   153  	newLink := NewLink(weight, in, n, false)
   154  	n.Incoming = append(n.Incoming, newLink)
   155  }
   156  
   157  // Recursively deactivate backwards through the network
   158  func (n *NNode) Flushback() {
   159  	n.ActivationsCount = 0
   160  	n.Activation = 0
   161  	n.lastActivation = 0
   162  	n.lastActivation2 = 0
   163  	n.isActive = false
   164  	n.visited = false
   165  }
   166  
   167  // Verify flushing for debugging
   168  func (n *NNode) FlushbackCheck() error {
   169  	if n.ActivationsCount > 0 {
   170  		return fmt.Errorf("NNODE: %s has activation count %d", n, n.ActivationsCount)
   171  	}
   172  	if n.Activation > 0 {
   173  		return fmt.Errorf("NNODE: %s has activation %f", n, n.Activation)
   174  	}
   175  	if n.lastActivation > 0 {
   176  		return fmt.Errorf("NNODE: %s has last_activation %f", n, n.lastActivation)
   177  	}
   178  	if n.lastActivation2 > 0 {
   179  		return fmt.Errorf("NNODE: %s has last_activation2 %f", n, n.lastActivation2)
   180  	}
   181  	return nil
   182  }
   183  
   184  // Find the greatest depth starting from this neuron at depth d
   185  func (n *NNode) Depth(d int) (int, error) {
   186  	if d > 1000 {
   187  		// to avoid infinite recursion
   188  		return 10, NetErrDepthCalculationFailedLoopDetected
   189  	}
   190  	n.visited = true
   191  	// Base Case
   192  	if n.IsSensor() {
   193  		return d, nil
   194  	} else {
   195  		// recursion
   196  		max := d // The max depth
   197  		for _, l := range n.Incoming {
   198  			if l.InNode.visited {
   199  				// was already visited (loop detected) - skipping
   200  				continue
   201  			}
   202  			curDepth, err := l.InNode.Depth(d + 1)
   203  			if err != nil {
   204  				return curDepth, err
   205  			}
   206  			if curDepth > max {
   207  				max = curDepth
   208  			}
   209  		}
   210  		return max, nil
   211  	}
   212  
   213  }
   214  
   215  // Convenient method to check network's node type (SENSOR, NEURON)
   216  func (n *NNode) NodeType() NodeType {
   217  	if n.IsSensor() {
   218  		return SensorNode
   219  	}
   220  	return NeuronNode
   221  }
   222  
   223  func (n *NNode) String() string {
   224  	activation, _ := utils.NodeActivators.ActivationNameFromType(n.ActivationType)
   225  	active := "active"
   226  	if !n.isActive {
   227  		active = "inactive"
   228  	}
   229  	return fmt.Sprintf("(%s id:%03d, %s, %s,\t%s -> step: %d = %.3f %.3f)",
   230  		NodeTypeName(n.NodeType()), n.Id, NeuronTypeName(n.NeuronType), activation, active,
   231  		n.ActivationsCount, n.Activation, n.Params)
   232  }
   233  
   234  // Prints all node's fields to the string
   235  func (n *NNode) Print() string {
   236  	str := "NNode fields\n"
   237  	b := bytes.NewBufferString(str)
   238  	_, _ = fmt.Fprintf(b, "\tId: %d\n", n.Id)
   239  	_, _ = fmt.Fprintf(b, "\tIsActive: %t\n", n.isActive)
   240  	_, _ = fmt.Fprintf(b, "\tActivation: %f\n", n.Activation)
   241  	activation, _ := utils.NodeActivators.ActivationNameFromType(n.ActivationType)
   242  	_, _ = fmt.Fprintf(b, "\tActivation Type: %s\n", activation)
   243  	_, _ = fmt.Fprintf(b, "\tNeuronType: %d\n", n.NeuronType)
   244  	_, _ = fmt.Fprintf(b, "\tActivationsCount: %d\n", n.ActivationsCount)
   245  	_, _ = fmt.Fprintf(b, "\tActivationSum: %f\n", n.ActivationSum)
   246  	_, _ = fmt.Fprintf(b, "\tIncoming: %s\n", n.Incoming)
   247  	_, _ = fmt.Fprintf(b, "\tOutgoing: %s\n", n.Outgoing)
   248  	_, _ = fmt.Fprintf(b, "\tTrait: %s\n", n.Trait)
   249  	_, _ = fmt.Fprintf(b, "\tPhenotypeAnalogue: %s\n", n.PhenotypeAnalogue)
   250  	_, _ = fmt.Fprintf(b, "\tParams: %f\n", n.Params)
   251  	_, _ = fmt.Fprintf(b, "\tlastActivation: %f\n", n.lastActivation)
   252  	_, _ = fmt.Fprintf(b, "\tlastActivation2: %f\n", n.lastActivation2)
   253  
   254  	return b.String()
   255  }