Represents a node in a graph.
Beispiel #1
0
        /// <summary>
        /// Override that paints nodes with a fill color that represents each node's activation function.
        /// </summary>
        protected override void PaintNode(GraphNode node, PaintState state)
        {
            Point nodePos = ModelToViewport(node.Position, state);
            if(!IsPointWithinViewport(nodePos, state))
            {   // Skip node. It's outside the viewport area.
                return;
            }

            // Paint the node as a square. Create a Rectangle that represents the square's position and size.
            Point p = new Point(nodePos.X - state._nodeDiameterHalf, nodePos.Y - state._nodeDiameterHalf);
            Size s = new Size(state._nodeDiameter, state._nodeDiameter);
            Rectangle r = new Rectangle(p, s);

            // Paint the node. Fill first and then border, this gives a clean border.
            Graphics g = state._g;
            int activationFnId = (int)node.AuxData[0];
            Brush fillBrush = _brushNodeFillArr[activationFnId % _brushNodeFillArr.Length];
            g.FillRectangle(fillBrush, r);
            g.DrawRectangle(__penBlack, r);

            // Draw the node tag.
            nodePos.X += state._nodeDiameterHalf+1;
            nodePos.Y -= state._nodeDiameterHalf/2;
            g.DrawString(node.Tag, __fontNodeTag, __brushBlack, nodePos);
        }
 private void UpdateModelBounds(GraphNode node, ref Size bounds)
 {
     if(node.Position.X > bounds.Width) {
         bounds.Width = node.Position.X;
     }
     if(node.Position.Y > bounds.Height) {
         bounds.Height = node.Position.Y;
     }
 }
        /// <summary>
        /// Create an IOGraph that represents the structure described by the provided INetworkDefinition.
        /// </summary>
        public IOGraph CreateGraph(INetworkDefinition networkDef)
        {
            // Perform depth analysis of network.
            NetworkDepthInfo depthInfo;
            if(networkDef.IsAcyclic)
            {
                AcyclicNetworkDepthAnalysis depthAnalysis = new AcyclicNetworkDepthAnalysis();
                depthInfo = depthAnalysis.CalculateNodeDepths(networkDef);
            }
            else
            {   
                CyclicNetworkDepthAnalysis depthAnalysis = new CyclicNetworkDepthAnalysis();
                depthInfo = depthAnalysis.CalculateNodeDepths(networkDef);
            }

            // Create an IOGraph, allocating storage for the node lists.
            INodeList nodeList = networkDef.NodeList;
            int nodeCount = nodeList.Count;
            int inputCount = networkDef.InputNodeCount + 1; // + to count bias as an input layer node.
            int outputCount = networkDef.OutputNodeCount;
            int hiddenCount = nodeCount - (inputCount + outputCount);
            IOGraph ioGraph = new IOGraph(inputCount, outputCount, hiddenCount, 0f, depthInfo._networkDepth);
            
            // We also build a dictionary of nodes keyed by innovation ID. This is used later
            // to assign connections to nodes.
            Dictionary<uint, GraphNode> nodeDict = new Dictionary<uint,GraphNode>(nodeCount);

            // Loop input nodes.
            int idx = 0;
            for(int i=0; i<inputCount; i++, idx++)
            {   // Create node, assign it a tag and add it to the node dictionary and the
                // input node list of the IOGraph.
                uint innovationId = nodeList[idx].Id;
                GraphNode node = new GraphNode(innovationId.ToString());
                node.AuxData = CreateGraphNodeAuxData(nodeList[idx]);
                node.Depth = depthInfo._nodeDepthArr[idx];
                nodeDict.Add(innovationId, node);
                ioGraph.InputNodeList.Add(node);
            }

            // Loop output nodes.
            for(int i=0; i<outputCount; i++, idx++)
            {   // Create node, assign it a tag and add it to the node dictionary and the
                // output node list of the IOGraph.
                uint innovationId = nodeList[idx].Id;
                GraphNode node = new GraphNode(innovationId.ToString());
                node.AuxData = CreateGraphNodeAuxData(nodeList[idx]);
                node.Depth = depthInfo._nodeDepthArr[idx];
                nodeDict.Add(innovationId, node);
                ioGraph.OutputNodeList.Add(node);
            }

            // Loop hidden nodes.
            for(; idx<nodeCount; idx++)
            {   // Create node, assign it a tag and add it to the node dictionary and the
                // hidden node list of the IOGraph.
                uint innovationId = nodeList[idx].Id;
                GraphNode node = new GraphNode(innovationId.ToString());
                node.AuxData = CreateGraphNodeAuxData(nodeList[idx]);
                node.Depth = depthInfo._nodeDepthArr[idx];
                nodeDict.Add(innovationId, node);
                ioGraph.HiddenNodeList.Add(node);
            }

            // Loop connections. Build GraphConnection objects and connect them to their source
            // and target nodes.
            double maxAbsWeight = 0.1; // Start at a non-zero value to prevent possibility of a divide by zero occurring.
            IConnectionList connectionList = networkDef.ConnectionList;
            int connCount = connectionList.Count;            
            for(int i=0; i<connCount; i++)
            {
                // Create connection object and assign its source and target nodes.
                INetworkConnection connection = connectionList[i];
                GraphNode sourceNode = nodeDict[connection.SourceNodeId];
                GraphNode targetNode = nodeDict[connection.TargetNodeId];
                GraphConnection conn = new GraphConnection(sourceNode, targetNode, (float)connection.Weight); 
    
                // Add the connection to the connection lists on the source and target nodes.
                sourceNode.OutConnectionList.Add(conn);
                targetNode.InConnectionList.Add(conn);

                // Track weight range over all connections.
                double absWeight = Math.Abs(connection.Weight);
                if(absWeight > maxAbsWeight) {
                    maxAbsWeight = absWeight;
                }
            }

            ioGraph.ConnectionWeightRange = (float)maxAbsWeight;
            return ioGraph;
        }
 /// <summary>
 /// Gets the state object for a given graph node. Creates the object if it does not yet exist.
 /// </summary>
 public ConnectionPointInfo GetNodeStateInfo(GraphNode node)
 {
     ConnectionPointInfo info;
     if(!_nodeStateDict.TryGetValue(node, out info))
     {
         info = new ConnectionPointInfo();
         _nodeStateDict.Add(node, info);
     }
     return info;
 }
Beispiel #5
0
        /// <summary>
        /// Layout nodes evenly spaced out on a grid.
        /// </summary>
        /// <param name="graph">The network/graph structure to be laid out.</param>
        /// <param name="layoutArea">The area the structure is to be laid out on.</param>
        public void Layout(IOGraph graph, Size layoutArea)
        {
            // Size struct to keep track of the area that bounds all nodes.
            Size bounds = new Size(0, 0);

            // Some other useful variables.
            int    inputCount       = graph.InputNodeList.Count;
            int    outputCount      = graph.OutputNodeList.Count;
            int    hiddenCount      = graph.HiddenNodeList.Count;
            double heightWidthRatio = (double)layoutArea.Height / (double)layoutArea.Width;

            // Determine how many layers/rows to arrange the nodes into.
            // Note. we always show the inputs and outputs in their own rows, therefore this just
            // handles how many additional rows are required for the hidden nodes.
            int numLayersHidden = 0;

            if (hiddenCount > 0)
            {   // Arrange nodes in a square and adjust for height/width ratio of layout area.
                double sqrtHidden = Math.Sqrt(hiddenCount);
                numLayersHidden = (int)Math.Floor(sqrtHidden * heightWidthRatio);

                // Must be at least 1 layer if we have nodes.
                numLayersHidden = Math.Max(1, numLayersHidden);
            }

            // Arrange the nodes.
            int yMarginTop    = (int)(layoutArea.Height * MarginProportionYTop);
            int yMarginBottom = (int)(layoutArea.Height * MarginProportionYBottom);
            int layoutHeight  = layoutArea.Height - (yMarginTop + yMarginBottom);
            int yCurrent      = yMarginTop;

            int yIncrement;

            if (0 == numLayersHidden)
            {   // Just two layers (input and output).
                yIncrement = layoutHeight;
            }
            else
            {
                yIncrement = layoutHeight / (numLayersHidden + 1);
            }

            // Input layer. Place all input nodes in one layer.
            int layoutWidth = layoutArea.Width - (2 * MarginX);
            int xIncrement  = layoutWidth / (inputCount + 1);
            int xCurrent    = MarginX + xIncrement;

            // Loop input nodes.
            for (int i = 0; i < inputCount; i++)
            {
                GraphNode node = graph.InputNodeList[i];
                node.Position = new Point(xCurrent, yCurrent);
                UpdateModelBounds(node, ref bounds);
                xCurrent += xIncrement;
            }

            // Increment yCurrent, ready for the next layer.
            yCurrent += yIncrement;

            // Layout hidden layers.
            if (0 != numLayersHidden)
            {
                // Calculate the max number of nodes in any hidden layer.
                int layerNodesMax = (int)Math.Ceiling((double)hiddenCount / (double)numLayersHidden);

                // Keep track of how many nodes remain to be positioned. The last layer will have fewer
                // than layerNodesMax if the number of nodes isn't a square number.
                int nodesRemaining = hiddenCount;
                int nodeIdx        = 0;

                // Loop layers.
                for (int i = 0; i < numLayersHidden; i++)
                {
                    // Calculate the number of nodes in this layer.
                    int layerNodeCount = Math.Min(nodesRemaining, layerNodesMax);

                    // Position nodes in this layer.
                    xIncrement = layoutWidth / (layerNodeCount + 1);
                    xCurrent   = MarginX + xIncrement;

                    // Loop nodes in this layer.
                    for (int j = 0; j < layerNodeCount; j++, nodeIdx++)
                    {
                        GraphNode node = graph.HiddenNodeList[nodeIdx];
                        node.Position = new Point(xCurrent, yCurrent);
                        UpdateModelBounds(node, ref bounds);
                        xCurrent += xIncrement;
                    }

                    // Increment yCurrent, ready for the next layer.
                    yCurrent       += yIncrement;
                    nodesRemaining -= layerNodeCount;
                }
            }

            // Output layer. Place all output nodes in one layer.
            xIncrement = layoutWidth / (outputCount + 1);
            xCurrent   = MarginX + xIncrement;

            // Loop output nodes.
            for (int i = 0; i < outputCount; i++)
            {
                GraphNode node = graph.OutputNodeList[i];
                node.Position = new Point(xCurrent, yCurrent);
                UpdateModelBounds(node, ref bounds);
                xCurrent += xIncrement;
            }

            // Store the bounds of the graph elements. Useful when drawing the graph.
            graph.Bounds = bounds;
        }