/// <summary> /// Reads a list of NetworkDefinition(s) from XML that has a containing 'Root' element. The root /// element also contains the activation function library that the genomes are associated with. /// </summary> /// <param name="xr">The XmlReader to read from.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be read. They are required /// for HyperNEAT genomes but not NEAT</param> public static List <NetworkDefinition> ReadCompleteNetworkDefinitionList(XmlReader xr, bool nodeFnIds) { // Find <Root>. XmlIoUtils.MoveToElement(xr, false, __ElemRoot); // Read IActivationFunctionLibrray. XmlIoUtils.MoveToElement(xr, true, __ElemActivationFunctions); IActivationFunctionLibrary activationFnLib = ReadActivationFunctionLibrary(xr); XmlIoUtils.MoveToElement(xr, false, __ElemNetworks); List <NetworkDefinition> networkDefList = new List <NetworkDefinition>(); using (XmlReader xrSubtree = xr.ReadSubtree()) { // Re-scan for the root <Networks> element. XmlIoUtils.MoveToElement(xrSubtree, false); // Move to first Network elem. XmlIoUtils.MoveToElement(xr, true, __ElemNetwork); // Read Network elements. do { NetworkDefinition networkDef = ReadNetworkDefinition(xr, activationFnLib, nodeFnIds); networkDefList.Add(networkDef); }while(xrSubtree.ReadToNextSibling(__ElemNetwork)); } return(networkDefList); }
/// <summary> /// Writes a single NetworkDefinition to XML within a containing 'Root' element and the activation function /// library that the genome is associated with. /// The XML is returned as a newly created XmlDocument. /// </summary> /// <param name="networkDef">The NetworkDefinition to save.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be emitted. They are required /// for HyperNEAT genomes but not for NEAT.</param> public static XmlDocument SaveComplete(NetworkDefinition networkDef, bool nodeFnIds) { XmlDocument doc = new XmlDocument(); using(XmlWriter xw = doc.CreateNavigator().AppendChild()) { WriteComplete(xw, networkDef, nodeFnIds); } return doc; }
/// <summary> /// Writes a single NetworkDefinition to XML. /// The XML is returned as a newly created XmlDocument. /// </summary> /// <param name="networkDef">The genome to save.</param> /// <param name="nodeFnIds">Indicates if node activation function IDs should be emitted. They are required /// for HyperNEAT genomes but not for NEAT.</param> public static XmlDocument Save(NetworkDefinition networkDef, bool nodeFnIds) { XmlDocument doc = new XmlDocument(); using (XmlWriter xw = doc.CreateNavigator().AppendChild()) { Write(xw, networkDef, nodeFnIds); } return(doc); }
/// <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; }