Hello.
I can't seem to figure out why the following code is producing a NullReferenceException. I have checked, and have confirmed that the arrays are in fact not null, all of the elements contain data. So why does VS think something's null? Thanks for your help.

// This line produces the error
NeuralNet net = new NeuralNet(CreateWeightsArray(NETWORK_INPUTS.Length), CreateFunctionsArray()); 


private Function[][] CreateFunctionsArray() // Functions are Enums
{
    // NEURONS_PER_LAYER is an int[]
    Function[][] functions = new Function[NEURONS_PER_LAYER.Length][]; 

    for (int x = 0; x < functions.Length; x++)
    {
        functions[x] = new Function[NEURONS_PER_LAYER[x]];
        for (int y = 0; y < functions[x].Length; y++)
            functions[x][y] = DEFAULT_FUNCTION; // Set functions to default function.
    }
    return functions;
}

private double[][][] CreateWeightsArray(int numInputs)
{
    double[][][] weights = new double[NEURONS_PER_LAYER.Length][][];

    for (int x = 0; x < weights.Length; x++)
    {
        weights[x] = new double[NEURONS_PER_LAYER[x]][];
        for (int y = 0; y < weights[x].Length; y++)
        {
            if(x == 0)
                weights[x][y] = new double[numInputs + 1];
            else
                weights[x][y] = new double[NEURONS_PER_LAYER[x - 1] + 1];

            for (int z = 0; z < weights[x][y].Length; z++) // Cycles through and sets all weights.
                weights[x][y][z] = rand.NextDouble();
        }
    }
    return weights;
}

Edited 4 Years Ago by kendaop

Can you post the constructor that is being called (NeuralNet(double[][][], Function[][]))? I'd also like to see what NEURONS_PER_LAYER is defined as and the values it contains, along with numInputs

Inline Code Example HereSure! Here you go. I'm assuming you meant NETWORK_ INPUTS instead of numinputs, as numinputs is the input parameter?

static int[] NEURONS_PER_LAYER = new int[] {4, 3, 1};
static double[] NETWORK_INPUTS = new double[] { 5.2, 6.1, 2.1, 1 };


//// From Kendaop.AI.ANN.NeuralNet \\\\
public NeuralNet(double[][][] inputWeights, Function[][] functions)
{
    CopyWeightsArray(ref inputWeights, ref weights);
    layers = new NeuronLayer[weights.Length];
    for (int x = 0; x < layers.Length; x++) 
        layers[x] = new NeuronLayer(functions[x], weights[x]);
}

protected void CopyWeightsArray(ref double[][][] source, ref double[][][] target)
    {
        target = new double[source.Length][][];
        for (int x = 0; x < source.Length; x++)
        {
            target[x] = new double[source[x].Length][];
            for (int y = 0; y < source[x].Length; y++)
            {
                target[x][y] = new double[source[x][y].Length];
                for (int z = 0; z < source[x][y].Length; z++)
                    target[x][y][z] = source[x][y][z];
            }
        }
    }

Edited 4 Years Ago by kendaop

Still missing parts of your code :) Is there any way you can post the whole class, or upload it somewhere?

Ok, here it is.

    class Controller
    {
        static double[] NETWORK_INPUTS = new double[] { 5.2, 6.1, 2.1, 1 };
        static int[] NEURONS_PER_LAYER = new int[] {4, 3, 1};
        static int H_PREFERRED_SPACE = 180, V_PREFERRED_SPACE = 60;
        static Function DEFAULT_FUNCTION = Function.Linear;
        NeuralNet net;
        Vector2[][] nodeLocations;
        Drawing drawing;
        Random rand;
        double[][] outputs;

        public Controller(GraphicsDevice GD)
        {
            rand = new Random();
            net = new NeuralNet(CreateWeightsArray(NETWORK_INPUTS.Length), CreateFunctionsArray());
            drawing = new Drawing(GD);
            outputs = new double[NEURONS_PER_LAYER.Length][];
            for(int x = 0; x < NEURONS_PER_LAYER.Length; x++)
                outputs[x] = new double[NEURONS_PER_LAYER[x]];
        }

        /// <summary>
        /// Creates a Function[][]. Change this code as necessary for testing of the ANN.
        /// </summary>
        /// <returns>Function[][]</returns>
        private Function[][] CreateFunctionsArray()
        {
            Function[][] functions = new Function[NEURONS_PER_LAYER.Length][];

            for (int x = 0; x < functions.Length; x++)
            {
                functions[x] = new Function[NEURONS_PER_LAYER[x]]; // Initialize second Function arrays.
                for (int y = 0; y < functions[x].Length; y++)
                    functions[x][y] = DEFAULT_FUNCTION; // Set functions.
            }
            return functions;
        }

        private double[][][] CreateWeightsArray(int numInputs)
        {
            double[][][] weights = new double[NEURONS_PER_LAYER.Length][][];

            for (int x = 0; x < weights.Length; x++) // Cycle through layers.
            {
                weights[x] = new double[NEURONS_PER_LAYER[x]][];
                for (int y = 0; y < weights[x].Length; y++) // Cycle through neurons.
                {
                    if(x == 0)
                        weights[x][y] = new double[numInputs + 1];
                    else
                        weights[x][y] = new double[NEURONS_PER_LAYER[x - 1] + 1];

                    for (int z = 0; z < weights[x][y].Length; z++) // Cycles through and sets all weights.
                        weights[x][y][z] = rand.NextDouble();
                }
            }
            return weights;
        }

        private void DrawNode(SpriteBatch spriteBatch, SpriteFont sf, Texture2D texture, Vector2 location, int layer, int node)
        {
            Vector2 imageLoc = new Vector2(location.X, location.Y);
            switch (net.Layers[layer].Neurons[node].ActivationFunction)
            {
                case Function.Linear:
                    spriteBatch.Draw(texture, imageLoc, Color.Green);
                    DrawString(spriteBatch, sf, outputs[layer][node].ToString(), 
                        new Vector2(imageLoc.X + 10, imageLoc.Y + 10), 0, Color.White, SpriteEffects.None);
                    break;
                case Function.Sigmoid:
                    spriteBatch.Draw(texture, imageLoc, Color.Blue);
                    break;
                default:
                    break;
            }
        }

        private void DrawNodeInputs(SpriteBatch sb, SpriteFont sf, Texture2D nodeTexture, Color color)
        {
            // Master loop oversees drawing of input lines for all except the first layer.
            for (int x = 1; x < net.Layers.Length; x++) // Cycles through layers.
                for (int y = 0; y < net.Layers[x].Neurons.Length; y++) // Cycles through neurons.
                    for(int z = 1; z < net.Layers[x].Neurons[y].Weights.Length; z++) // Cycles through weights.
                    {
                        Vector2 targetVect = new Vector2(nodeLocations[x][y].X, nodeLocations[x][y].Y + nodeTexture.Height / 2);
                        Vector2 originVect = new Vector2(nodeLocations[x - 1][y].X + nodeTexture.Width,
                            nodeLocations[x - 1][z - 1].Y + nodeTexture.Height / 2);
                        float angle = drawing.DrawLine(sb, 2, color, originVect, targetVect);
                        DrawString(sb, sf, net.Layers[x].Neurons[y].Weights[z].ToString(), 
                            drawing.IntermediatePoint(originVect, targetVect, 0.2f * y + 0.2f), angle, Color.Bisque, SpriteEffects.None);
                    }
        }

        private void DrawString(SpriteBatch sb, SpriteFont sf, string text, Vector2 position, float angle, Color color, SpriteEffects sFX)
        {
            sb.DrawString(sf, text, position, color, angle, Vector2.Zero, 0.75f, sFX, 0f);
        }

        private void CalculateNodeLocations(int screenHeight, int screenWidth, int textureHeight, int textureWidth)
        {
            nodeLocations = new Vector2[NEURONS_PER_LAYER.Length][];

            for (int x = 0; x < NEURONS_PER_LAYER.Length; x++) // Cycles through layers.
            {
                nodeLocations[x] = new Vector2[NEURONS_PER_LAYER[x]]; // Create this layer's Vector2 array.

                for (int y = 0; y < NEURONS_PER_LAYER[x]; y++) // Cycles through neurons.
                {
                    // Stores the desired locations of the nodes.
                    nodeLocations[x][y] = new Vector2(CoordinateFunctionX(screenWidth, textureWidth, x + 1, NEURONS_PER_LAYER.Length) + 100,
                        CoordinateFunctionY(screenHeight, textureHeight, y + 1, NEURONS_PER_LAYER[x]));
                }
            }
        }

        private int CoordinateFunctionX(double screenDimension, double tileDimension, int index, int maxIndex)
        {
            return (int)((screenDimension / 2) + ((2 * index - (double)(maxIndex + 2)) * tileDimension / 2) +
                ((2 * index - (double)(maxIndex + 1)) * H_PREFERRED_SPACE / 2));
        }

        private int CoordinateFunctionY(double screenDimension, double tileDimension, int index, int maxIndex)
        {
            return (int)((screenDimension / 2) + ((2 * index - (double)(maxIndex + 2)) * tileDimension / 2) +
                ((2 * index - (double)(maxIndex + 1)) * V_PREFERRED_SPACE / 2));
        }

        public void Draw(SpriteBatch sb, SpriteFont sf, Texture2D texture, Texture2D blank, int screenHeight, int screenWidth)
        {
            CalculateNodeLocations(screenHeight, screenWidth, texture.Height, texture.Width);

            sb.Begin();
            for (int x = 0; x < NEURONS_PER_LAYER.Length; x++) // Cycles through layers.
                for (int y = 0; y < NEURONS_PER_LAYER[x]; y++) // Cycles through neurons.
                {
                    DrawNode(sb, sf, texture, nodeLocations[x][y], x, y);
                    DrawNodeInputs(sb, sf, texture, Color.White);
                }
            sb.End();
        }

        public void Update()
        {
            outputs = net.Update(NETWORK_INPUTS);
        }
    }
This article has been dead for over six months. Start a new discussion instead.