Search code examples
lualua-table

Why is Lua treating tables as numbers?


I was playing around with Lua and neural networks, and I stumbled across a weird bug in my code, where Lua appears to be treating a table as a number. I have the following code...

function CreateEmptyNeuron()
    local neuron = {
    val = 0,
    forward = { },
    backward = { },
}
    return neuron;
end
--layerSize is an integer representing the size of the layer we're creating.
function CreateLayer(layerSize)
    local layer = {}
    for i = 1, layerSize, 1 do
        local n = CreateEmptyNeuron();
        table.insert(layer, n)
    end
    return layer;
end

--layerSize is actually an array of integers representing the array sizes of
--each individual layer (first layer is the input layer, last layer is output layer)
function CreateLayers(numLayers, layerSize)
    local layers = {}
    for i = 1, numLayers, 1 do
        local layer = CreateLayer(layerSize[i]);
        table.insert(layers, layer)
    end
    return layers;
end

--This function initializes the "val" variable in each table, and 
--forward connects each neuron to every node in the next layer...
function ForwardConnectLayers(network)
    for i = 1, #network, 1 do
        local layer = network[i]
        local next_layer = nil
        if (i+1) < #network then
            next_layer = network[i+1]
        else
            print("We have reached the output layer...")
            next_layer = nil
        end
        for j = 1, #layer, 1 do
            local neuron = layer[j]
            neuron.val = (math.random() + math.random(0, 100))/100;
            if next_layer ~= nil then
                for x = 1, #next_layer, 1 do
                    neuron.forward[x] = math.random(1, 100)/100
                    print("Found forward layer...")
                end
            else
                print("We cannot forward connect the output layer...\n")
            end
        end
    end
end

function BackwardConnectLayers(network)
    for i = 1, #network, 1 do
        local layer = network[i]
        local prev_layer = nil
        if (i-1) > 1 then
            prev_layer = network[i-1]
        else
            print("We have reached the input layer...")
            prev_layer = nil
        end
        for j = #layer, 1, -1 do
            local neuron = layer[j]
            --neuron.val = (math.random() + math.random(0, 100))/100;
            if prev_layer ~= nil then
                for x = 1, #prev_layer, 1 do
                    table.insert(neuron.backward, prev_layer[x])
                    print("Found input layer...")
                end
            else
                print("We cannot backward connect the input layer...\n")
            end
        end
    end
end

function CreateBrain()
    local LAYER_SIZES = {
        10, 20, 20, 5
    }
    local brain = CreateLayers(4, LAYER_SIZES)
    ForwardConnectLayers(brain)
    BackwardConnectLayers(brain)
    return brain;
end

AI = CreateBrain();
AI.Run = function(inputs, expectedOutputs)
    local input_layer = AI[1]
    local output_layer = AI[#AI]
    for i = 0, #inputs, 1 do
        input_layer[i] = inputs[i]
    end

    --For each layer in the network...
    for l = 1, #AI, 1 do
        --Get the next layer and this layer...
        local this_layer = AI[l]
        local next_layer = AI[l+1]
    
        --And for each neuron in the next layer... 
        --Multiply the value of the neuron in this layer by 
        --the value of the modifier, and set the value of the next
        --Neuron to be nextneuron.val + sum(<x>.val*<x>[k])
        if next_layer ~= nil then
            for m = 1, #next_layer, 1 do
                local prev_layer_sum = 0
                for n = 1, #this_layer, 1 do
                    local neuron = this_layer[n]
                    print(neuron)
                end
            end
        end
    end
end

ai_inputs = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }
expected_outputs = { 8, 8, 8, 8, 8 }
AI.Run(ai_inputs, expected_outputs)

When I run the code as shown, it, as expected, prints a list of addresses to memory for Lua tables:

The memory addresses of Lua tables...

But then, if I alter AI.Run to the following:

AI.Run = function(inputs, expectedOutputs)
    local input_layer = AI[1]
    local output_layer = AI[#AI]
    for i = 0, #inputs, 1 do
        input_layer[i] = inputs[i]
    end

    --For each layer in the network...
    for l = 1, #AI, 1 do
        --Get the next layer and this layer...
        local this_layer = AI[l]
        local next_layer = AI[l+1]
    
        --And for each neuron in the next layer... 
        --Multiply the value of the neuron in this layer by 
        --the value of the modifier, and set the value of the next
        --Neuron to be nextneuron.val + sum(<x>.val*<x>[k])
        if next_layer ~= nil then
            for m = 1, #next_layer, 1 do
                local prev_layer_sum = 0
                for n = 1, #this_layer, 1 do
                    local neuron = this_layer[n]
                    for k, v in pairs(neuron) do
                        print(k .. ", " .. v)
                    end
                end
            end
        end
    end
end

I get the following error message:

No description available

Indicating that Lua thinks the table is a number... which is, suffice to say, confusing. There's probably some language technicality defining this behavior, but I can't find good documentation surrounding this (in part because I don't even know what term to search; I'm not usually a Lua programmer).

EDIT:

So, it appears, somewhere in the code, numbers are getting inserted into the layers...

(Link to online Lua compiler where you can run the code: http://tpcg.io/_WPRPQV)


Solution

  • Your AI.Run function does

    local input_layer = AI[1]
    for i = 0, #inputs, 1 do
        input_layer[i] = inputs[i]
    end
    

    where inputs is a table of numbers, i.e. the first layer of your network is replaced by just numbers.

    You probably want to replace the val of the neuron in that layer instead:

    local input_layer = AI[1]
    for i = 0, #inputs, 1 do
        input_layer[i].val = inputs[i]
    end