/// <summary> /// Create a network definition by querying the provided IBlackBox (typically a CPPN) with the /// substrate connection endpoints. /// </summary> /// <param name="blackbox">The HyperNEAT CPPN that defines the strength of connections between nodes on the substrate.</param> /// <param name="lengthCppnInput">Optionally we provide a connection length input to the CPPN.</param> public INetworkDefinition CreateNetworkDefinition(IBlackBox blackbox, bool lengthCppnInput) { // Get the sequence of substrate connections. Either a pre-built list or a dynamically // generated sequence. IEnumerable<SubstrateConnection> connectionSequence = _connectionList ?? GetConnectionSequence(); // Iterate over substrate connections. Determine each connection's weight and create a list // of network definition connections. ISignalArray inputSignalArr = blackbox.InputSignalArray; ISignalArray outputSignalArr = blackbox.OutputSignalArray; ConnectionList networkConnList = new ConnectionList(_connectionCountHint); int lengthInputIdx = inputSignalArr.Length - 1; foreach(SubstrateConnection substrateConn in connectionSequence) { int layerToLayerAdder = ((int)substrateConn._tgtNode._position[2] * 3); // Assign the connection's endpoint position coords to the CPPN/blackbox inputs. Note that position dimensionality is not fixed. for(int i=0; i<_dimensionality - 1; i++) { inputSignalArr[i] = substrateConn._srcNode._position[i]; inputSignalArr[i + _dimensionality - 1] = substrateConn._tgtNode._position[i]; inputSignalArr[i + 2 * (_dimensionality - 1)] = Math.Abs(substrateConn._srcNode._position[i] - substrateConn._tgtNode._position[i]); } // Optional connection length input. if(lengthCppnInput) { inputSignalArr[lengthInputIdx] = CalculateConnectionLength(substrateConn._srcNode._position, substrateConn._tgtNode._position); } // Reset blackbox state and activate it. blackbox.ResetState(); blackbox.Activate(); // Read connection weight from output 0. double weight = outputSignalArr[0 + layerToLayerAdder]; // Skip connections with a weight magnitude less than _weightThreshold. double weightAbs = Math.Abs(weight); if (outputSignalArr[2 + layerToLayerAdder] >= 0) { // For weights over the threshold we re-scale into the range [-_maxWeight,_maxWeight], // assuming IBlackBox outputs are in the range [-1,1]. weight = (weightAbs) * _weightRescalingCoeff * Math.Sign(weight); // Create network definition connection and add to list. networkConnList.Add(new NetworkConnection(substrateConn._srcNode._id, substrateConn._tgtNode._id, weight)); } } // Additionally we create connections from each hidden and output node to a bias node that is not defined at any // position on the substrate. The motivation here is that a each node's input bias is independent of any source // node (and associated source node position on the substrate). That we refer to a bias 'node' is a consequence of how input // biases are handled in NEAT - with a specific bias node that other nodes can be connected to. int setCount = _nodeSetList.Count; for(int i=1; i<setCount; i++) { SubstrateNodeSet nodeSet = _nodeSetList[i]; foreach(SubstrateNode node in nodeSet.NodeList) { // Assign the node's position coords to the blackbox inputs. The CPPN inputs for source node coords are set to zero when obtaining bias values. for(int j=0; j<_dimensionality - 1; j++) { inputSignalArr[j] = 0.0; inputSignalArr[j + _dimensionality - 1] = node._position[j]; } // Optional connection length input. if(lengthCppnInput) { inputSignalArr[lengthInputIdx] = CalculateConnectionLength(node._position); } // Reset blackbox state and activate it. blackbox.ResetState(); blackbox.Activate(); // Read bias connection weight from output 1. double weight = outputSignalArr[1+ (i- 1) * 3]; // Skip connections with a weight magnitude less than _weightThreshold. double weightAbs = Math.Abs(weight); if(weightAbs > _weightThreshold) { // For weights over the threshold we re-scale into the range [-_maxWeight,_maxWeight], // assuming IBlackBox outputs are in the range [-1,1]. weight = (weightAbs - _weightThreshold) * _weightRescalingCoeff * Math.Sign(weight); // Create network definition connection and add to list. Bias node is always ID 0. networkConnList.Add(new NetworkConnection(0, node._id, weight)); } } } // Check for no connections. // If no connections were generated then there is no point in further evaulating the network. // However, null is a valid response when decoding genomes to phenomes, therefore we do that here. if(networkConnList.Count == 0) { return null; } // Construct and return a network definition. NetworkDefinition networkDef = new NetworkDefinition(_inputNodeCount, _outputNodeCount, _activationFnLibrary, _netNodeList, networkConnList); // Check that the definition is valid and return it. Debug.Assert(networkDef.PerformIntegrityCheck()); return networkDef; }