Why is Lua treating tables as numbers? - lua

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:
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:
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)

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

Related

Why is all the outputs from my Neural Network the same when using the ReLU activation function?

When using the ReLU activation function in my Neural Network I noticed all the outputs were the same. The Sigmoid activation function works just fine. Is there something wrong with the ReLU function? Why is this happening? What can I do to fix this?
The example is a simple XOR Neural Network written in Lua.
math.randomseed(os.time())
local nn = require("NeuralNetwork")
network = nn.new(2,1,1,4,0.2, "ReLU")
local x=0
local attempts = 100000
for i = 1,attempts do
x+=1
if x > 10000 then
wait()
x=0
end
network:backPropagate({0, 0}, {0})
network:backPropagate({1, 0}, {1})
network:backPropagate({0, 1}, {1})
network:backPropagate({1, 1}, {0})
end
print("0 0 | "..network:feedForward({0,0})[1])
print("1 0 | "..network:feedForward({1,0})[1])
print("0 1 | "..network:feedForward({0,1})[1])
print("1 1 | "..network:feedForward({1,1})[1])
Output:
0 0 | 0.48780487804878037
1 0 | 0.48780487804878037
0 1 | 0.48780487804878037
1 1 | 0.48780487804878037
- Library -
local nn = {}
nn.__index = nn
nn.ActivationFunctions = {
sigmoid = function(x) return 1/(1+math.exp(-x)) end,
ReLU = function(x) return math.max(0, x) end,
}
nn.Derivatives = {
sigmoid = function(x) return x * (1 - x) end,
ReLU = function(x) return x >= 0 and 1 or 0 end,
}
nn.CostFunctions = {
MSE = function(outputs, expected)
local sum = 0
for i = 1, #outputs do
sum += (expected[i] - outputs[i])^2
end
return sum/#outputs
end,
}
function nn.new(inputs, outputs, hiddenLayers, neurons, learningRate, activationFunction)
local self = setmetatable({}, nn)
self.learningRate = learningRate or .3
self.activationFunction = activationFunction or "ReLu"
self.net = {}
local net = self.net
local layers = hiddenLayers+2
for i = 1, layers do
net[i] = {}
end
for i = 1, inputs do
net[1][i] = {value = 0}
end
for i = 2, layers-1 do
for x = 1, neurons do
net[i][x] = {netInput = 0, value = 0, bias = math.random()*2-1, weights = {}}
for z = 1, #net[i-1] do
net[i][x].weights[z] = math.random()*2-1
end
end
end
for i = 1, outputs do
net[layers][i] = {netInput = 0, value = 0, bias = math.random()*2-1, weights = {}}
for z = 1, #net[layers-1] do
net[layers][i].weights[z] = math.random()*2-1
end
end
return self
end
function nn.newFromRawData(data)
return setmetatable(data, nn)
end
function nn:feedForward(inputs)
local net = self.net
local activation = self.activationFunction
local layers = #net
local inputLayer = net[1]
local outputLayer = net[layers]
for i = 1, #inputLayer do
inputLayer[i].value = inputs[i]
end
for i = 2, layers do
local layer = net[i]
for x = 1, #layer do
local sum = layer[x].bias
for z = 1, #net[i-1] do
sum += net[i-1][z].value * layer[x].weights[z]
end
layer[x].netInput = sum
layer[x].value = nn.ActivationFunctions[activation](sum)
end
end
local outputs = {}
for i = 1, #outputLayer do
table.insert(outputs, outputLayer[i].value)
end
return outputs
end
function nn:backPropagate(inputs, expected)
local outputs = self:feedForward(inputs)
--print(outputs)
local net = self.net
local activation = self.activationFunction
local layers = #net
local lr = self.learningRate
local inputLayer = net[1]
local outputLayer = net[layers]
for i = 1, #outputLayer do
local delta = -(expected[i] - outputs[i]) * nn.Derivatives[activation](outputs[i])
outputLayer[i].delta = delta
end
for i = layers-1, 2, -1 do
local layer = net[i]
local nextLayer = net[i+1]
for x = 1, #layer do
local delta = 0
for z = 1, #nextLayer do
delta += nextLayer[z].delta * nextLayer[z].weights[x]
end
layer[x].delta = delta * nn.Derivatives[activation](layer[x].value)
end
end
for i = 2, layers do
local lastLayer = net[i-1]
for x = 1, #net[i] do
net[i][x].bias -= lr * net[i][x].delta
for z = 1, #lastLayer do
net[i][x].weights[z] -= lr * net[i][x].delta * lastLayer[z].value
end
end
end
end
return nn

Attempt to index field (a nil value), for an Object

I have a problem with trying to add things to a LOVE2D version of Match-3. (From the CS50 course)
I added a function swapTiles() into my Board class and made a class object called self.board in a Class called PlayState. Then when I try to access the new function, it says this error:
Error
src/states/PlayState.lua:155: attempt to index field 'board' (a nil value)
I'll provide my Board and PlayState class below:
Board: (keep in mind the new function is literally in the code)
Board = Class{}
function Board:init(x, y, level) -- Added "level" as an integer for the block variations.
self.x = x
self.y = y
self.matches = {}
self.level = level
self:initializeTiles()
end
function Board:swapTiles(tile1, tile2)
-- swap grid positions of tiles
local tempX = tile1.gridX
local tempY = tile1.gridY
tile1.gridX = tile2.gridX
tile1.gridY = tile2.gridY
tile2.gridX = tempX
tile2.gridY = tempY
-- swap tiles in the tiles table
self.tiles[tile1.gridY][tile1.gridX] = tile1
self.tiles[tile2.gridY][tile2.gridX] = tile2
end
function Board:initializeTiles()
self.tiles = {}
-- There should only be two shiny tiles.
for tileY = 1, 8 do
-- empty table that will serve as a new row
table.insert(self.tiles, {})
for tileX = 1, 8 do
self.isPowerup = false
if math.random(1, 25) == 4 then
self.isPowerup = true
end
-- create a new tile at X,Y with a random color and variety
table.insert(self.tiles[tileY], Tile(tileX, tileY, math.random(18), math.min(8, math.random(1, self.level)), self.isPowerup))
end
end
while self:calculateMatches() do
-- recursively initialize if matches were returned so we always have
-- a matchless board on start
self:initializeTiles()
end
end
--[[
Goes left to right, top to bottom in the board, calculating matches by counting consecutive
tiles of the same color. Doesn't need to check the last tile in every row or column if the
last two haven't been a match.
]]
function Board:calculateMatches()
local matches = {}
-- how many of the same color blocks in a row we've found
local matchNum = 1
-- horizontal matches first
for y = 1, 8 do
local colorToMatch = self.tiles[y][1].color
matchNum = 1
-- every horizontal tile
for x = 2, 8 do
-- if this is the same color as the one we're trying to match...
if self.tiles[y][x].color == colorToMatch then
matchNum = matchNum + 1
else
-- set this as the new color we want to watch for
colorToMatch = self.tiles[y][x].color
-- if we have a match of 3 or more up to now, add it to our matches table
if matchNum >= 3 then
local match = {}
-- go backwards from here by matchNum
for x2 = x - 1, x - matchNum, -1 do
-- add each tile to the match that's in that match
table.insert(match, self.tiles[y][x2])
-- Shiny Check
if self.tiles[y][x2].isShiny == true then
for i = 1, 8 do
table.insert(match, self.tiles[y][i])
end
end
end
-- add this match to our total matches table
table.insert(matches, match)
end
-- don't need to check last two if they won't be in a match
if x >= 7 then
break
end
matchNum = 1
end
end
-- account for the last row ending with a match
if matchNum >= 3 then
local match = {}
-- go backwards from end of last row by matchNum
for x = 8, 8 - matchNum + 1, -1 do
table.insert(match, self.tiles[y][x])
end
table.insert(matches, match)
end
end
-- vertical matches
for x = 1, 8 do
local colorToMatch = self.tiles[1][x].color
matchNum = 1
-- every vertical tile
for y = 2, 8 do
if self.tiles[y][x].color == colorToMatch then
matchNum = matchNum + 1
else
colorToMatch = self.tiles[y][x].color
if matchNum >= 3 then
local match = {}
for y2 = y - 1, y - matchNum, -1 do
table.insert(match, self.tiles[y2][x])
if self.tiles[y2][x].isShiny == true then
for i = 1, 8 do
table.insert(match, self.tiles[i][x])
end
end
end
table.insert(matches, match)
end
matchNum = 1
-- don't need to check last two if they won't be in a match
if y >= 7 then
break
end
end
end
-- account for the last column ending with a match
if matchNum >= 3 then
local match = {}
-- go backwards from end of last row by matchNum
for y = 8, 8 - matchNum, -1 do
table.insert(match, self.tiles[y][x])
end
table.insert(matches, match)
end
end
-- store matches for later reference
self.matches = matches
-- return matches table if > 0, else just return false
return #self.matches > 0 and self.matches or false
end
--[[
Remove the matches from the Board by just setting the Tile slots within
them to nil, then setting self.matches to nil.
]]
function Board:removeMatches()
for k, match in pairs(self.matches) do
for k, tile in pairs(match) do
self.tiles[tile.gridY][tile.gridX] = nil
end
end
self.matches = nil
end
--[[
Shifts down all of the tiles that now have spaces below them, then returns a table that
contains tweening information for these new tiles.
]]
function Board:getFallingTiles()
-- tween table, with tiles as keys and their x and y as the to values
local tweens = {}
-- for each column, go up tile by tile till we hit a space
for x = 1, 8 do
local space = false
local spaceY = 0
local y = 8
while y >= 1 do
-- if our last tile was a space...
local tile = self.tiles[y][x]
if space then
-- if the current tile is *not* a space, bring this down to the lowest space
if tile then
-- put the tile in the correct spot in the board and fix its grid positions
self.tiles[spaceY][x] = tile
tile.gridY = spaceY
-- set its prior position to nil
self.tiles[y][x] = nil
-- tween the Y position to 32 x its grid position
tweens[tile] = {
y = (tile.gridY - 1) * 32
}
-- set space back to 0, set Y to spaceY so we start back from here again
space = false
y = spaceY
spaceY = 0
end
elseif tile == nil then
space = true
if spaceY == 0 then
spaceY = y
end
end
y = y - 1
end
end
-- create replacement tiles at the top of the screen
for x = 1, 8 do
for y = 8, 1, -1 do
local tile = self.tiles[y][x]
-- if the tile is nil, we need to add a new one
if not tile then
local tile = Tile(x, y, math.random(18), math.random(1, self.level))
tile.y = -32
self.tiles[y][x] = tile
tweens[tile] = {
y = (tile.gridY - 1) * 32
}
end
end
end
return tweens
end
function Board:getNewTiles()
return {}
end
function Board:testForMatches()
for y = 1, 8 do
for x = 1, 8 do
-- Test for left swap
if x > 1 then
end
end
end
end
function Board:render()
for y = 1, #self.tiles do
for x = 1, #self.tiles[1] do
self.tiles[y][x]:render(self.x, self.y)
end
end
end
Here's my PlayState: (look for PlayState:swapTiles(), keep in mind that the self.board is being used several times for and works fine, except when i try calling self.board:swapTiles().)
PlayState = Class{__includes = BaseState}
function PlayState:init()
-- start our transition alpha at full, so we fade in
self.transitionAlpha = 255
-- position in the grid which we're highlighting
self.boardHighlightX = 0
self.boardHighlightY = 0
-- timer used to switch the highlight rect's color
self.rectHighlighted = false
-- flag to show whether we're able to process input (not swapping or clearing)
self.canInput = true
-- tile we're currently highlighting (preparing to swap)
self.highlightedTile = nil
self.score = 0
self.timer = 60
-- set our Timer class to turn cursor highlight on and off
Timer.every(0.5, function()
self.rectHighlighted = not self.rectHighlighted
end)
-- subtract 1 from timer every second
Timer.every(1, function()
self.timer = self.timer - 1
-- play warning sound on timer if we get low
if self.timer <= 5 then
gSounds['clock']:play()
end
end)
end
function PlayState:enter(params)
-- grab level # from the params we're passed
self.level = params.level
-- spawn a board and place it toward the right
self.board = params.board or Board(VIRTUAL_WIDTH - 272, 16)
-- grab score from params if it was passed
self.score = params.score or 0
-- score we have to reach to get to the next level
self.scoreGoal = self.level * 1.25 * 1000
end
function PlayState:update(dt)
if love.keyboard.wasPressed('escape') then
love.event.quit()
end
-- go back to start if time runs out
if self.timer <= 0 then
-- clear timers from prior PlayStates
Timer.clear()
gSounds['game-over']:play()
gStateMachine:change('game-over', {
score = self.score
})
end
-- go to next level if we surpass score goal
if self.score >= self.scoreGoal then
-- clear timers from prior PlayStates
-- always clear before you change state, else next state's timers
-- will also clear!
Timer.clear()
gSounds['next-level']:play()
-- change to begin game state with new level (incremented)
gStateMachine:change('begin-game', {
level = self.level + 1,
score = self.score
})
end
if self.canInput then
-- move cursor around based on bounds of grid, playing sounds
if love.keyboard.wasPressed('up') then
self.boardHighlightY = math.max(0, self.boardHighlightY - 1)
gSounds['select']:play()
elseif love.keyboard.wasPressed('down') then
self.boardHighlightY = math.min(7, self.boardHighlightY + 1)
gSounds['select']:play()
elseif love.keyboard.wasPressed('left') then
self.boardHighlightX = math.max(0, self.boardHighlightX - 1)
gSounds['select']:play()
elseif love.keyboard.wasPressed('right') then
self.boardHighlightX = math.min(7, self.boardHighlightX + 1)
gSounds['select']:play()
end
-- if we've pressed enter, to select or deselect a tile...
if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then
-- if same tile as currently highlighted, deselect
local x = self.boardHighlightX + 1
local y = self.boardHighlightY + 1
-- if nothing is highlighted, highlight current tile
if not self.highlightedTile then
self.highlightedTile = self.board.tiles[y][x]
-- if we select the position already highlighted, remove highlight
elseif self.highlightedTile == self.board.tiles[y][x] then
self.highlightedTile = nil
-- if the difference between X and Y combined of this highlighted tile
-- vs the previous is not equal to 1, also remove highlight
elseif math.abs(self.highlightedTile.gridX - x) + math.abs(self.highlightedTile.gridY - y) > 1 then
gSounds['error']:play()
self.highlightedTile = nil
else
self:swapTiles(self.highlightedTile, self.board.tiles[y][x], true)
end
end
end
Timer.update(dt)
end
function PlayState:swapTiles(tile1, tile2, swapBackAtNoMatch)
local tile1 = tile1
local tile2 = tile2
local swapBackAtNoMatch = swapBackAtNoMatch
self.board:swapTiles(tile1, tile2) -- Causes the nil error.
if swapBackAtNoMatch then
-- tween coordinates between two swapping tiles
Timer.tween(0.1, {
[tile1] = {x = tile2.x, y = tile2.y},
[tile2] = {x = tile1.x, y = tile1.y}
})
-- once they've swapped, tween falling blocks
:finish(function ()
local matches = self.board:calculateMatches()
if matches then
self.calculateMatches(matches)
else
-- swap back if there's no match
self.swapTiles(tile1, tile2, false)
gSounds['error']:play()
end
end)
else
-- tween coordinates between the two so they swap
Timer.tween(0.1, {
[tile1] = {x = tile2.x, y = tile2.y},
[tile2] = {x = tile1.x, y = tile1.y}})
end
end
--[[
Calculates whether any matches were found on the board and tweens the needed
tiles to their new destinations if so. Also removes tiles from the board that
have matched and replaces them with new randomized tiles, deferring most of this
to the Board class.
]]
function PlayState:calculateMatches()
self.highlightedTile = nil
-- if we have any matches, remove them and tween the falling blocks that result
local matches = self.board:calculateMatches()
if matches then
gSounds['match']:stop()
gSounds['match']:play()
-- add score for each match
for k, match in pairs(matches) do
local varietyPoints = 0 -- We'll keep track of the bonus variety points here
-- We'll use vareity to calculate points for each tile within a match
for j, tiles in pairs(match) do
varietyPoints = varietyPoints + tiles.variety * 25
end
self.score = self.score + (#match * 50) + varietyPoints
-- Also add one second times the number of match to the timer
self.timer = self.timer + #match * 1
end
-- remove any tiles that matched from the board, making empty spaces
self.board:removeMatches()
-- gets a table with tween values for tiles that should now fall
local tilesToFall = self.board:getFallingTiles()
-- first, tween the falling tiles over 0.25s
Timer.tween(0.25, tilesToFall):finish(function()
local newTiles = self.board:getNewTiles()
-- then, tween new tiles that spawn from the ceiling over 0.25s to fill in
-- the new upper gaps that exist
Timer.tween(0.25, newTiles):finish(function()
-- recursively call function in case new matches have been created
-- as a result of falling blocks once new blocks have finished falling
self:calculateMatches()
end)
end)
-- if no matches, we can continue playing
else
self.canInput = true
end
end
Honestly doesn't make much sense. Please help!
function PlayState:swapTiles(tile1, tile2, swapBackAtNoMatch) end
is syntactic sugar for
PlayState.swaptiles = function(self, tile1, tile2, swapBackAtNoMatch) end
That's the reason why you can work with self inside that function.
A function defined like that needs to be called using the colon operator as well to make this work. Or you explicitly provide the table as first parameter.
Hence in your call self.swapTiles(tile1, tile2, false)
self is going to be tile1
tile1.board is nil so self.board is nil in this function call which causes the error
You have to call self:swapTiles(tile1, tile2, false) or self.swapTiles(self, tile1, tile2, false)
Please make sure you fully understand the colon syntax.

I can't really figure out what's wrong with my neural network class (Lua)

local matrix = require("matrixx")
local Class = require("class")
NeuralNetwork = Class{}
function NeuralNetwork:init(input_nodes, hidden_nodes, output_nodes)
self.input_nodes = input_nodes
self.hidden_nodes = hidden_nodes
self.output_nodes = output_nodes
self.weights_ih = matrix(self.hidden_nodes, self.input_nodes, math.random())
self.weights_ho = matrix(self.output_nodes, self.hidden_nodes, math.random())
self.bias_h = matrix(self.hidden_nodes, 1, math.random())
self.bias_o = matrix(self.output_nodes, 1, math.random())
self.learning_rate = 0.1
end
function NeuralNetwork:feedforward(input_array)
--Generating the Hidden Outputs
local inputs = matrix(input_array)
for i=1, #input_array do
inputs[i][1] = input_array[i]
end
local hidden = self.weights_ih * inputs
hidden = hidden + self.bias_h
--Activation Function
hidden = matrix.map(hidden, tanh)
--Generating the output's output
local output = self.weights_ho * hidden
output = output + self.bias_o
output = matrix.map(output, tanh)
return output
end
function NeuralNetwork:train(input_array, target_array)
--Generating the Hidden Outputs
local inputs = matrix(input_array)
for i=1, #input_array do
inputs[i][1] = input_array[i]
end
local hidden = self.weights_ih * inputs
hidden = hidden + self.bias_h
--Activation Function
hidden = matrix.map(hidden, tanh)
--Generating the output's output
local outputs = self.weights_ho * hidden
outputs = outputs + self.bias_o
outputs = matrix.map(outputs, tanh)
--Convert Targets Array to Matrix object
local targets = matrix(#target_array, 1)
for i=1, #target_array do
targets[i][1] = target_array[i]
end
--Calculate the error
local output_errors = targets - outputs
--Calculate gradient
local gradients = matrix.map(outputs, tanhd)
gradients = gradients * output_errors
gradients = gradients * self.learning_rate
-- Calculate deltas
local hidden_T = matrix.transpose(hidden)
local weight_ho_deltas = gradients * hidden_T
-- Adjust the weights by deltas
self.weights_ho = self.weights_ho + weight_ho_deltas
-- Adjust the bias by its deltas (which is just the gradients)
self.bias_o = self.bias_o + gradients
-- Calculate the hidden layer errors
local who_t = matrix.transpose(self.weights_ho)
local hidden_errors = who_t * output_errors
-- Calculate hidden gradient
local hidden_gradient = matrix.map(hidden, tanhd)
hidden_gradient = hidden_gradient * hidden_errors * self.learning_rate
-- Calcuate input->hidden deltas
local inputs_T = matrix.transpose(inputs)
local weight_ih_deltas = hidden_gradient * inputs_T
self.weights_ih = self.weights_ih + weight_ih_deltas
-- Adjust the bias by its deltas (which is just the gradients)
self.bias_h = self.bias_h + hidden_gradient
self.weights_ih:print()
print()
self.weights_ho:print()
print()
end
function sigmoid(x)
return 1 / (1 + math.exp(-x))
end
function dsigmoid(x)
return sigmoid(x) * (1 - sigmoid(x))
end
function tanh(x)
return (math.exp(x) - math.exp(-x)) / (math.exp(x) + math.exp(-x))
end
function tanhd(x)
return 1 / math.cosh(x)^2
end
--MAIN
local nn = NeuralNetwork(2, 2, 1)
local training_data = {
{
inputs = {0, 1},
target = {1}
},
{
inputs = {1, 1},
target = {0}
},
{
inputs = {1, 0},
target = {1}
},
{
inputs = {0, 0},
target = {0}
}
}
for i = 1, 30 do
local data = training_data[math.floor(math.random(#training_data))]
nn:train(data.inputs, data.target)
end
nn:feedforward({0, 1}):print()
nn:feedforward({1, 1}):print()
nn:feedforward({0, 0}):print()
nn:feedforward({1, 0}):print()
I wrote this NeuralNetwork class.
I used a class library and a matrix library
Respectively class matrix
Seems like it's all correct to me (ideally, at least), btw when I instantiate a new NN with 2 inputs, 2 hidden neurons and 1 output and try to solve a XOR, it doesn't work.
What am i missing? Maybe I misunderstood the matrix library, hope someone can help me
EDIT:
I added a map function in the library to apply a math function to every number in a matrix.
function matrix.map( m1, func )
local mtx = {}
for i = 1,#m1 do
mtx[i] = {}
for j = 1,#m1[1] do
mtx[i][j] = func(m1[i][j])
end
end
return setmetatable( mtx, matrix_meta )
end

Corona SDK / Lua : An table's property is nil, when accessed via event.other during collision event. But why?

So I have this module, where all of its activity during the game is in. In t.physics I add a collision event listener (differentiating if target is a group or a single object). When the concerning objects detect a collision though, the property col of the other object (event.other) seems to be nil, although I initially set it to a string representing a color in t.create. I just can't find the cause for that, can anyone?
Thanks for your help.
Greetings, Nils
local fence = require("lib.fence")
local physics = require("physics")
local t = {}
local stages = {yellow = 1, lila = 1, red = 1}
local sizes = {1, 3.625, 7.25}
t.colors = {"yellow", "lila", "red"}
t.growing = false
t.setSize = function(fill)
local tHeight = fill.contentHeight * sizes[stages[fill.col]]
local tScale = tHeight / fill.contentHeight
fill.yScale = tScale
end
t.grow = function(group, color, hero)
local counter = 0
stages[color] = stages[color] + 1
for i = 1, group.numChildren, 1 do
if group[i].col == color then
counter = counter + 1
local function newPhysics() t.physics(group) end
if counter == 1 then
local function reset() t.growing = false if stages[color] == 3 then stages[color] = 1; newPhysics(); end end
local function start() t.growing = true end
transition.to(group[i], {time = 260, yScale = sizes[stages[color]], onStart = start, onComplete = reset})
else
transition.to(group[i], {time = 250, yScale = sizes[stages[color]], onStart = start})
end
end
end
end
t.physics = function(target)
if target.numChildren == nil then
physics.removeBody(target)
local function add()
physics.addBody( target, "static", {isSensor = true} )
target.collision = function(self, event)
if event.phase == "began" then
target.count = target.count + 1
if target.count == 1 then
t.grow(target.parent, self.col, event.other)
end
elseif event.phase == "ended" then
target.count = 0
end
end
end
timer.performWithDelay(1, add, 1)
else
for i = 1, target.numChildren, 1 do
physics.removeBody( target[i] )
physics.addBody( target[i], "static", {isSensor = true} )
target[i].name = "fill"
local fill = target[i]
fill.count = 0
fill.collision = function(self, event)
if event.phase == "began" then
self.count = self.count + 1
if self.count == 1 and event.other.x ~= nil then
t.grow(target, self.col, event.other)
end
else
fill.count = 0
end
end
fill:addEventListener("collision")
end
end
end
t.setColor = function(fill)
local colors = {
{238 / 255, 228 / 255, 28 / 255},
{38 / 255, 33 / 255, 77 / 255},
{175 / 255, 24 / 255, 52 / 255},
}
local names = {"yellow", "lila", "red"}
local r = math.random(3)
fill.fill = colors[r]
fill.col = names[r]
end
t.create = function(fences, group, colors)
local fills = {}
for i = 1, #fences, 1 do
local rCol = math.random(3)
local col
if rCol == 1 then
col = colors.yellow
elseif rCol == 2 then
col = colors.lila
else
col = colors.red
end
fills[i] = display.newRect(
group, fences[i].x + fences[i].contentWidth * 0.125, fences[i].y,
fences[i].contentWidth * 0.9, (fences[i].contentHeight * 0.5 / 3)
)
fills[i].dPosX = fills[i].x
fills[i].y = display.contentHeight- fills[i].contentHeight / 2
fills[i].fill = col
fills[i].col = t.colors[rCol]
fills[i].increased = false
end
return fills
end
t.move = function(fills, fences, group)
for i = 1, #fills, 1 do
local fill = fills[i]
function fill:enterFrame()
self:translate(fence.speed, 0)
if t.growing == false then
t.setSize(self)
end
if self.x > display.contentWidth + 0.55 * fences[i].contentWidth then
local xT = {}
for i = 1, group.numChildren, 1 do
xT[i] = group[i].x
end
local function compare(a, b) return a < b end
table.sort(xT, compare)
self.x = xT[1] - fences[i].contentWidth * 0.98
t.setColor(self)
local function newPhysics() t.physics(self) end
timer.performWithDelay( 25, newPhysics, 1 )
self:toBack()
end
end
Runtime:addEventListener("enterFrame", fill)
end
end
return t
Solved. Ugh, sorry, I forgot to define the property on the other object involved (hero), that has its own module. What a stupid slip.
Thanks for your answers anyways!
You don't seem to have any dynamic bodies here. What is colliding with what? Could it be that the other object involved in the collision (the value of event.other) is not something initialized in t.create() and so doesn't have the col property?
From the Corona documentation on Collision Detection:
Some body types will — or will not — collide with other body types. In a collision between two physical objects, at least one of the objects must be dynamic, since this is the only body type which collides with any other type.
Also, in your fill.collision(), I think you want to pass event.target as the first argument to t.grow() rather than target. If you try things, please update the question with more information.

Table value doesn't change

I have a 2 dim array and all it's cells filled with zeros.
What i'm trying to do is to take some randomly chosen cells and fill it with 4 or 5
but what i get is either empty gird with all value equal to zero or i get just one value that has changed to 4 or 5 and that's my code below:
local grid = {}
for i=1,10 do
grid[i] = {}
for j=1,10 do
grid[i][j] = 0
end
end
local empty={}
for i=1,10 do
for j=1,10 do
if grid[i][j]==0 then
table.insert(empty,i ..'-'.. j)
end
end
end
local fp=math.floor(table.maxn(empty)/3)
local fx,fy
for i=1,fp do
math.randomseed(os.time())
math.random(0,1)
local fo=math.random(0,1)
math.random(table.maxn(empty))
local temp= empty[math.random(table.maxn(empty))]
local dashindex=string.find(temp,'-')
fx=tonumber(string.sub(temp,1,dashindex-1))
fy=tonumber(string.sub(temp,dashindex+1,string.len(temp)))
if fo==0 then
grid[fx][fy]=4
elseif fo==1 then
grid[fx][fy]=5
end
end
for i=1,10 do
for j=1,10 do
print(grid[i][j])
end
print('\n')
end
I'm not sure what the for i=1,fp loop is doing with temp and fo, for example the seed should only be set once, and also, the return value on line after local fo is ignored, seems very messy. But based on your post, if you really just want to randomly select N cells from your 2D array and set those to either 4 or 5 (randomly), this should work:
-- maybe N = fp
local N = 5
math.randomseed(os.time())
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] == 0 then
grid[fx][fy] = math.random(4,5)
i = i + 1
end
until i > N
Note however that the closer N is to number of items in array (100 in your example), the longer it will take for the loop to complete. If this is a concern, then for large N values, you could do the opposite: initialize each cell to 4 or 5 randomly, and then randomly set size - N of them to 0.
math.randomseed(os.time())
local rows = 10
local columns = 10
local grid = {}
if N > rows*columns/2 then
for i=1,rows do
grid[i] = {}
for j=1,columns do
grid[i][j] = math.random(4,5)
end
end
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] ~= 0 then
grid[fx][fy] = 0
i = i + 1
end
until i > N
else
for i=1,rows do
grid[i] = {}
for j=1,columns do
grid[i][j] = 0
end
end
local i = 1
repeat
fx = math.random(1, 10)
fy = math.random(1, 10)
if grid[fx][fy] == 0 then
grid[fx][fy] = math.random(4,5)
i = i + 1
end
until i > N
end

Resources