Represents a [weighted and directed] graph of connected nodes with nodes divided into three types/groups; Input nodes, output nodes and hidden nodes.
        /// <summary>
        /// Layout nodes based on their depth within the network.
        /// 
        /// Note 1.
        /// Input nodes are defined as being at layer zero and we position them in their own layer at the 
        /// top of the layout area. Any other type of node (hidden or output) node not connected to is also
        /// defined as being at layer zero, if such nodes exist then we place them into their own layout 
        /// layer to visually separate them from the input nodes.
        /// 
        /// Note 2.
        /// Output nodes can be at a range of depths, but for clarity we position them all in their own layer 
        /// at the bottom of the layout area. A hidden node can have a depth greater than or equal to one or 
        /// more of the output nodes, to handle this case neatly we ensure that the output nodes are always 
        /// in a layer by themselves by creating an additional layer in the layout if necessary.
        ///
        /// Note 3.
        /// Hidden nodes are positioned into layers between the inputs and outputs based on their depth.
        /// 
        /// Note 4. 
        /// Empty layers are not possible in the underlying network because for there to be a layer N node it 
        /// must have a connection from a layer N-1 node. However, in cyclic networks the output nodes can be
        /// source nodes, but we also always paint output nodes in their own layout layer at the bottom of the
        /// layout area. Therefore if the only node at a given depth is an output node then the layout layer 
        /// can be empty. To handle this neatly we check for empty layout layers before invoking the layout logic.
        /// </summary>
        /// <param name="graph">The network/graph structure to be layed out.</param>
        /// <param name="layoutArea">The area the structure is to be layed out on.</param>
        public void Layout(IOGraph graph, Size layoutArea)
        {
            int inputCount = graph.InputNodeList.Count;
            int outputCount = graph.OutputNodeList.Count;
            int hiddenCount = graph.HiddenNodeList.Count;

        //=== Stratify all nodes into layout layers, each layer keyed by depth value.
            SortedDictionary<int,List<GraphNode>> layersByDepth = new SortedDictionary<int,List<GraphNode>>();

            // Special case. Key all input nodes by depth -1. This places them as the first layer and distinct 
            // from all other nodes.
            layersByDepth.Add(-1, graph.InputNodeList);

            // Stratify hidden nodes into layers.
            // Count hidden nodes in each layer.
            int[] hiddenNodeCountByLayerArr = new int[graph.Depth];
            for(int i=0; i<hiddenCount; i++) {
                hiddenNodeCountByLayerArr[graph.HiddenNodeList[i].Depth]++;
            }

            // Allocate storage per layer.
            for(int i=0; i<graph.Depth; i++) 
            {
                int nodeCount = hiddenNodeCountByLayerArr[i];
                if(0 != nodeCount) {
                    layersByDepth.Add(i, new List<GraphNode>(nodeCount));
                }
            }

            // Put hidden nodes into layer lists.
            for(int i=0; i<hiddenCount; i++) 
            {
                GraphNode node = graph.HiddenNodeList[i];
                layersByDepth[node.Depth].Add(node);
            }

            // Special case 2. Place output nodes in their own distinct layer at a depth one more than the max
            // depth of the network. This places them as the last layer and distinct from all other nodes.
            layersByDepth.Add(graph.Depth, graph.OutputNodeList);

        //=== Layout.
            // Calculate height of each layer. We divide the layout area height by the number of layers, but also 
            // define margins at the top and bottom of the view. To make best use of available height the margins 
            // are defined as a proportion of the layer height. Hence we have:
            //
            // ============================
            // d - network depth.
            // l - layer height.
            // m - margin height (same height used for top and bottom margins).
            // p - proportion of layer height that gives margin height.
            // H - layout areas height
            // ============================
            //
            // Derivation:
            // ============================ 
            // 1) l = (H-2m) / (d-1)
            //
            // 2) m = lp, therefore
            //
            // 3) l = (H-2pl) / (d-1), solve for l
            //
            // 4) l = H/(d-1) - 2pl/(d-1)
            //
            // 5) 1 = H/(d-1)l - 2p/(d-1)
            //
            // 6) H / (d-1)l = 1 + 2p/(d-1), inverting both sides gives
            //
            // 7) (d-1)l / H = 1/[ 1 + 2p/(d-1) ]
            //
            // 8)            = (d-1) / ((d-1) + 2p)
            //
            // 9)            = (d-1) / (d + 2p - 1), rearranging for l gives
            //
            // 10) l = H / (d + 2p - 1)
            const float p = 0.33f;
            const float p2 = 2f * p;
            // Rounding will produce 'off by one' errors, e.g. all heights may not total heinght of layout area,
            // but the effect is probably negligible on the quality of the layout.
            int layoutLayerCount = layersByDepth.Count;
            int l = (int)Math.Round((float)layoutArea.Height / ((float)layoutLayerCount + p2 - 1f));
            int m = (int)Math.Round(l * p);

            // Size struct to keep track of the area that bounds all nodes.
            Size bounds = new Size(0,0);

            // Assign a position to each node, one layer at a time.
            int yCurrent = m;
            foreach(List<GraphNode> layerNodeList in layersByDepth.Values)
            {
                // Calculate inter-node gap and margin width (we use the same principle as with the vertical layout of layers, see notes above).
                int nodeCount = layerNodeList.Count;
                float xIncr = (float)layoutArea.Width / ((float)nodeCount + p2 - 1f);
                int xMargin = (int)Math.Round(xIncr * p);

                // Loop nodes in layer; Assign position to each.
                float xCurrent = xMargin;
                for(int nodeIdx=0; nodeIdx < nodeCount; nodeIdx++, xCurrent += xIncr) 
                {
                    layerNodeList[nodeIdx].Position = new Point((int)xCurrent, yCurrent);
                    UpdateModelBounds(layerNodeList[nodeIdx], ref bounds);
                }

                // Increament y coord for next layer.
                yCurrent += l;
            }

            // Store the bounds of the graph elements. Useful when drawing the graph.
            graph.Bounds = bounds;
        }
示例#2
0
        /// <summary>
        /// Paints the provided IOGraph onto the provided GDI+ Graphics drawing surface.
        /// </summary>
        protected override void PaintNetwork(IOGraph graph, PaintState state)
        {
            // Invoke the base painting routine.
            base.PaintNetwork(graph, state);

            // Paint a legend for the activation functions.
            PaintLegend(state);
        }
示例#3
0
        /// <summary>
        /// Paints the provided IOGraph onto the provided GDI+ Graphics drawing surface.
        /// </summary>
        protected override void PaintNetwork(IOGraph graph, PaintState state)
        {
            // Invoke the base painting routine.
            base.PaintNetwork(graph, state);

            // Paint a legend for the activation functions.
            PaintLegend(state);
        }
 /// <summary>
 /// Paints the provided IOGraph onto the provided GDI+ Graphics drawing surface.
 /// </summary>
 public void PaintNetwork(IOGraph graph, Graphics g, Rectangle viewportArea, float zoomFactor)
 {
     // Create a PaintState object. This holds all temporary state info for the painting routines.
     // Pass the call on to the virtual PaintNetwork. This allows us to override a version of PaintNetwork 
     // that has access to a PaintState object.
     PaintState state = new PaintState(g, viewportArea, zoomFactor, graph.ConnectionWeightRange);
     PaintNetwork(graph, state);
 }
示例#5
0
        /// <summary>
        /// Paints the provided IOGraph onto the provided GDI+ Graphics drawing surface.
        /// </summary>
        public void PaintNetwork(IOGraph graph, Graphics g, Rectangle viewportArea, float zoomFactor)
        {
            // Create a PaintState object. This holds all temporary state info for the painting routines.
            // Pass the call on to the virtual PaintNetwork. This allows us to override a version of PaintNetwork
            // that has access to a PaintState object.
            PaintState state = new PaintState(g, viewportArea, zoomFactor, graph.ConnectionWeightRange);

            PaintNetwork(graph, state);
        }
        /// <summary>
        /// Paints the provided IOGraph onto the current GDI+ Graphics drawing surface.
        /// </summary>
        protected virtual void PaintNetwork(IOGraph graph, PaintState state)
        {
            // Create per-node state info.
            int hiddenNodeCount = graph.HiddenNodeList.Count;
            int inputNodeCount = graph.InputNodeList.Count;
            int outputNodeCount = graph.OutputNodeList.Count;
            state._nodeStateDict = new Dictionary<GraphNode,ConnectionPointInfo>(hiddenNodeCount + inputNodeCount + outputNodeCount);

            // Paint all connections. We do this first and paint nodes on top of the connections. This allows the 
            // slightly messy ends of the connections to be painted over by the nodes.
            PaintConnections(graph.InputNodeList, state);
            PaintConnections(graph.HiddenNodeList, state);
            PaintConnections(graph.OutputNodeList, state);

            // Paint all nodes. Painted over the top of connection endpoints.
            PaintNodes(graph.InputNodeList, state);
            PaintNodes(graph.HiddenNodeList, state);
            PaintNodes(graph.OutputNodeList, state);
        }
示例#7
0
        /// <summary>
        /// Paints the provided IOGraph onto the current GDI+ Graphics drawing surface.
        /// </summary>
        protected virtual void PaintNetwork(IOGraph graph, PaintState state)
        {
            // Create per-node state info.
            int hiddenNodeCount = graph.HiddenNodeList.Count;
            int inputNodeCount  = graph.InputNodeList.Count;
            int outputNodeCount = graph.OutputNodeList.Count;

            state._nodeStateDict = new Dictionary <GraphNode, ConnectionPointInfo>(hiddenNodeCount + inputNodeCount + outputNodeCount);

            // Paint all connections. We do this first and paint nodes on top of the connections. This allows the
            // slightly messy ends of the connections to be painted over by the nodes.
            PaintConnections(graph.InputNodeList, state);
            PaintConnections(graph.HiddenNodeList, state);
            PaintConnections(graph.OutputNodeList, state);

            // Paint all nodes. Painted over the top of connection endpoints.
            PaintNodes(graph.InputNodeList, state);
            PaintNodes(graph.HiddenNodeList, state);
            PaintNodes(graph.OutputNodeList, state);
        }
 /// <summary>
 /// Constructs with the provided graph painter and layout manager.
 /// </summary>
 public IOGraphViewportPainter(IOGraphPainter graphPainter, ILayoutManager layoutManager)
 {
     _graph         = null;
     _layoutManager = layoutManager;
     _graphPainter  = graphPainter;
 }
 /// <summary>
 /// Constructs with the provided graph painter and a default layout manager.
 /// </summary>
 public IOGraphViewportPainter(IOGraphPainter graphPainter)
 {
     _graph         = null;
     _layoutManager = new DepthLayoutManager();
     _graphPainter  = graphPainter;
 }
 /// <summary>
 /// Constructs with the provided graph painter and layout manager.
 /// </summary>
 public IOGraphViewportPainter(IOGraphPainter graphPainter, ILayoutManager layoutManager)
 {
     _graph = null;
     _layoutManager = layoutManager;
     _graphPainter = graphPainter;
 }
 /// <summary>
 /// Constructs with the provided graph painter and a default layout manager.
 /// </summary>
 public IOGraphViewportPainter(IOGraphPainter graphPainter)
 {
     _graph = null;
     _layoutManager = new DepthLayoutManager();
     _graphPainter = graphPainter;
 }
        /// <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 occuring.
            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 oevr all connections.
                double absWeight = Math.Abs(connection.Weight);
                if (absWeight > maxAbsWeight)
                {
                    maxAbsWeight = absWeight;
                }
            }

            ioGraph.ConnectionWeightRange = (float)maxAbsWeight;
            return(ioGraph);
        }
示例#13
0
        /// <summary>
        /// Layout nodes based on their depth within the network.
        ///
        /// Note 1.
        /// Input nodes are defined as being at layer zero and we position them in their own layer at the
        /// top of the layout area. Any other type of node (hidden or output) node not connected to is also
        /// defined as being at layer zero, if such nodes exist then we place them into their own layout
        /// layer to visually separate them from the input nodes.
        ///
        /// Note 2.
        /// Output nodes can be at a range of depths, but for clarity we position them all in their own layer
        /// at the bottom of the layout area. A hidden node can have a depth greater than or equal to one or
        /// more of the output nodes, to handle this case neatly we ensure that the output nodes are always
        /// in a layer by themselves by creating an additional layer in the layout if necessary.
        ///
        /// Note 3.
        /// Hidden nodes are positioned into layers between the inputs and outputs based on their depth.
        ///
        /// Note 4.
        /// Empty layers are not possible in the underlying network because for there to be a layer N node it
        /// must have a connection from a layer N-1 node. However, in cyclic networks the output nodes can be
        /// source nodes, but we also always paint output nodes in their own layout layer at the bottom of the
        /// layout area. Therefore if the only node at a given depth is an output node then the layout layer
        /// can be empty. To handle this neatly we check for empty layout layers before invoking the layout logic.
        /// </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)
        {
            int inputCount  = graph.InputNodeList.Count;
            int outputCount = graph.OutputNodeList.Count;
            int hiddenCount = graph.HiddenNodeList.Count;

            //=== Stratify all nodes into layout layers, each layer keyed by depth value.
            SortedDictionary <int, List <GraphNode> > layersByDepth = new SortedDictionary <int, List <GraphNode> >();

            // Special case. Key all input nodes by depth -1. This places them as the first layer and distinct
            // from all other nodes.
            layersByDepth.Add(-1, graph.InputNodeList);

            // Stratify hidden nodes into layers.
            // Count hidden nodes in each layer.
            int[] hiddenNodeCountByLayerArr = new int[graph.Depth];
            for (int i = 0; i < hiddenCount; i++)
            {
                hiddenNodeCountByLayerArr[graph.HiddenNodeList[i].Depth]++;
            }

            // Allocate storage per layer.
            for (int i = 0; i < graph.Depth; i++)
            {
                int nodeCount = hiddenNodeCountByLayerArr[i];
                if (0 != nodeCount)
                {
                    layersByDepth.Add(i, new List <GraphNode>(nodeCount));
                }
            }

            // Put hidden nodes into layer lists.
            for (int i = 0; i < hiddenCount; i++)
            {
                GraphNode node = graph.HiddenNodeList[i];
                layersByDepth[node.Depth].Add(node);
            }

            // Special case 2. Place output nodes in their own distinct layer at a depth one more than the max
            // depth of the network. This places them as the last layer and distinct from all other nodes.
            layersByDepth.Add(graph.Depth, graph.OutputNodeList);

            //=== Layout.
            // Calculate height of each layer. We divide the layout area height by the number of layers, but also
            // define margins at the top and bottom of the view. To make best use of available height the margins
            // are defined as a proportion of the layer height. Hence we have:
            //
            // ============================
            // d - network depth.
            // l - layer height.
            // m - margin height (same height used for top and bottom margins).
            // p - proportion of layer height that gives margin height.
            // H - layout areas height
            // ============================
            //
            // Derivation:
            // ============================
            // 1) l = (H-2m) / (d-1)
            //
            // 2) m = lp, therefore
            //
            // 3) l = (H-2pl) / (d-1), solve for l
            //
            // 4) l = H/(d-1) - 2pl/(d-1)
            //
            // 5) 1 = H/(d-1)l - 2p/(d-1)
            //
            // 6) H / (d-1)l = 1 + 2p/(d-1), inverting both sides gives
            //
            // 7) (d-1)l / H = 1/[ 1 + 2p/(d-1) ]
            //
            // 8)            = (d-1) / ((d-1) + 2p)
            //
            // 9)            = (d-1) / (d + 2p - 1), rearranging for l gives
            //
            // 10) l = H / (d + 2p - 1)
            const float p  = 0.9f;
            const float p2 = 2f * p;
            // Rounding will produce 'off by one' errors, e.g. all heights may not total height of layout area,
            // but the effect is probably negligible on the quality of the layout.
            int layoutLayerCount = layersByDepth.Count;
            int l = (int)Math.Round((float)layoutArea.Height / ((float)layoutLayerCount + p2 - 1f));
            int m = (int)Math.Round(l * p);

            // Size struct to keep track of the area that bounds all nodes.
            Size bounds = new Size(0, 0);

            // Assign a position to each node, one layer at a time.
            int yCurrent = m;

            foreach (List <GraphNode> layerNodeList in layersByDepth.Values)
            {
                // Calculate inter-node gap and margin width (we use the same principle as with the vertical layout of layers, see notes above).
                int   nodeCount = layerNodeList.Count;
                float xIncr     = (float)layoutArea.Width / ((float)nodeCount + p2 - 1f);
                int   xMargin   = (int)Math.Round(xIncr * p);

                // Loop nodes in layer; Assign position to each.
                float xCurrent = xMargin;
                for (int nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++, xCurrent += xIncr)
                {
                    layerNodeList[nodeIdx].Position = new Point((int)xCurrent, yCurrent);
                    UpdateModelBounds(layerNodeList[nodeIdx], ref bounds);
                }

                // Increment y coord for next layer.
                yCurrent += l;
            }

            // Store the bounds of the graph elements. Useful when drawing the graph.
            graph.Bounds = bounds;
        }
        /// <summary>
        /// Layout nodes evenly spaced out on a grid.
        /// </summary>
        /// <param name="graph">The network/graph structure to be layed out.</param>
        /// <param name="layoutArea">The area the structrue is to be layed 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 requried for the hidden nodes.
            int numLayersHidden=0;

            if(hiddenCount>0)
            {   // Arrange nodes in a sqare 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;
        }
示例#15
0
        /// <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>
        /// Layout nodes evenly spaced out on a grid.
        /// </summary>
        /// <param name="graph">The network/graph structure to be layed out.</param>
        /// <param name="layoutArea">The area the structrue is to be layed 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 requried for the hidden nodes.
            int numLayersHidden = 0;

            if (hiddenCount > 0)
            {   // Arrange nodes in a sqare 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;
        }